精华内容
下载资源
问答
  • 如何建立时间序列预测卷积神经网络模型前言概述单变量CNN模型数据准备CNN模型多元CNN模型多输入序列 前言 卷积神经网络模型,简称CNNs,可以应用于时间序列预测。 有许多类型CNN模型可以用于每一种特定类型...

    如何建立时间序列预测的卷积神经网络模型


    前言

    卷积神经网络模型,简称CNNs,可以应用于时间序列预测。

    有许多类型的CNN模型可以用于每一种特定类型的时间序列预测问题。

    在本教程中,您将了解如何为一系列标准时间序列预测问题开发一套CNN模型。

    本教程的目标是提供关于每种类型的时间序列问题的每个模型的独立示例,作为模板,您可以复制并适应特定的时间序列预测问题。

    完成本教程后,您将了解:

    • 如何开发用于单变量时间序列预测的CNN模型。
    • 如何建立多元时间序列预测的CNN模型。
    • 如何建立CNN模型进行多步时间序列预测。

    这是一篇大而重要的文章;你可能想把它作为书签,以备将来参考。


    提示:以下是本篇文章正文内容,下面案例可供参考

    概述

    在本文中,我们将探讨如何为时间序列预测开发一套不同类型的CNN模型。

    这些模型是在小的人为时间序列问题上演示的,目的是给出所处理的时间序列问题类型的特征。选择的模型配置是任意的,不是针对每个问题进行优化的;这不是目标。

    本文分为四个部分,分别是

    1. 单变量CNN模型
    2. 多元CNN模型
    3. 多步CNN模型
    4. 多变量多步CNN模型

    单变量CNN模型

    尽管传统的CNNs是针对二维图像数据开发的,但CNNs可以用来模拟单变量时间序列预测问题。

    单变量时间序列是由具有时间顺序的单个观测序列组成的数据集,需要一个模型从过去的观测序列中学习,以预测序列中的下一个值。

    本节分为两部分,分别是:

    1. 数据准备
    2. CNN模型

    数据准备

    在一个单变量序列可以被建模之前,它必须被准备好。

    CNN模型将学习一个函数,该函数将一系列过去的观测值映射为一个输出观测值的输入。因此,观测序列必须转化为多个实例,模型可以从中学习。

    考虑一个给定的单变量序列:

    [10, 20, 30, 40, 50, 60, 70, 80, 90]
    

    我们可以将序列分成多个输入/输出模式,称为样本,其中三个时间步用作输入,一个时间步用作输出,用于正在学习的一步预测。

    X,				y
    10, 20, 30		40
    20, 30, 40		50
    30, 40, 50		60
    

    下面的split_sequence()函数实现此行为,并将给定的单变量序列拆分为多个样本,其中每个样本都有指定数量的时间步,并且输出是单个时间步。

    # split a univariate sequence into samples
    def split_sequence(sequence, n_steps):
    	X, y = list(), list()
    	for i in range(len(sequence)):
    		# find the end of this pattern
    		end_ix = i + n_steps
    		# check if we are beyond the sequence
    		if end_ix > len(sequence)-1:
    			break
    		# gather input and output parts of the pattern
    		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
    		X.append(seq_x)
    		y.append(seq_y)
    	return array(X), array(y)
    

    我们可以在上面的小型人工数据集上演示这个函数。

    下面列出了完整的示例。

    # univariate data preparation
    from numpy import array
    
    # split a univariate sequence into samples
    def split_sequence(sequence, n_steps):
    	X, y = list(), list()
    	for i in range(len(sequence)):
    		# find the end of this pattern
    		end_ix = i + n_steps
    		# check if we are beyond the sequence
    		if end_ix > len(sequence)-1:
    			break
    		# gather input and output parts of the pattern
    		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
    		X.append(seq_x)
    		y.append(seq_y)
    	return array(X), array(y)
    
    # define input sequence
    raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
    # choose a number of time steps
    n_steps = 3
    # split into samples
    X, y = split_sequence(raw_seq, n_steps)
    # summarize the data
    for i in range(len(X)):
    	print(X[i], y[i])
    

    运行该示例将单变量序列拆分为六个样本,其中每个样本有三个输入时间步和一个输出时间步。

    [10 20 30] 40
    [20 30 40] 50
    [30 40 50] 60
    [40 50 60] 70
    [50 60 70] 80
    [60 70 80] 90
    

    既然我们知道了如何准备一个用于建模的单变量序列,那么让我们来看看开发一个CNN模型,它可以学习输入到输出的映射。

    CNN模型

    一维CNN是一种CNN模型,它有一个卷积隐藏层,在一维序列上运行。在某些情况下,这之后可能是第二卷积层,例如很长的输入序列,然后是池层,其工作是将卷积层的输出提取到最显著的元素。

    卷积层和池层之后是一个密集的完全连接层,该层解释了模型卷积部分提取的特征。在卷积层和稠密层之间使用平坦层将特征映射简化为单个一维向量。

    我们可以为单变量时间序列预测定义一个一维CNN模型,如下所示。

    # define model
    model = Sequential()
    model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
    model.add(MaxPooling1D(pool_size=2))
    model.add(Flatten())
    model.add(Dense(50, activation='relu'))
    model.add(Dense(1))
    model.compile(optimizer='adam', loss='mse')
    

    定义中的关键是输入的形状;这就是模型所期望的每个样本输入的时间步数和特征数。

    我们使用的是一个单变量序列,所以特征的数量是一个变量。

    作为输入的时间步数是我们在准备数据集作为split_sequence()函数的参数时选择的数。

    每个样本的输入形状在第一个隐藏层的定义上的输入形状参数中指定。

    我们几乎总是有多个样本,因此,模型希望输入部分的训练数据的具有以下维度或形状:

    [samples, timesteps, features]
    

    上一节中的split_sequence()函数将输出具有形状[samples,timesteps]的X,所以我们可以很容易地重塑它,为一个特征增加一个维度

    # reshape from [samples, timesteps] into [samples, timesteps, features]
    n_features = 1
    X = X.reshape((X.shape[0], X.shape[1], n_features))
    

    CNN实际上并没有将数据视为具有时间步长,而是将其视为可以执行卷积读取操作的序列,如一维图像。

    在本例中,我们定义了一个具有64个过滤器映射和2个内核大小的卷积层。然后是一个最大池层和一个密集层来解释输入特征。指定了预测单个数值的输出层。

    该模型采用有效的Adam随机梯度下降法进行拟合,并采用均方误差(mse)损失函数进行优化。

    一旦定义了模型,我们就可以将其拟合到训练数据集上。

    # fit model
    model.fit(X, y, epochs=1000, verbose=0)
    

    在模型拟合之后,我们可以利用它进行预测。

    我们可以通过提供输入来预测序列中的下一个值:

    [70, 80, 90]
    

    并期望模型能预测:

    [100]
    

    该模型期望输入形状是三维的,具有[样本、时间步长、特征],因此在进行预测之前必须对单个输入样本进行整形。

    # demonstrate prediction
    x_input = array([70, 80, 90])
    x_input = x_input.reshape((1, n_steps, n_features))
    yhat = model.predict(x_input, verbose=0)
    

    我们可以将所有这些联系在一起,演示如何开发用于单变量时间序列预测的一维CNN模型,并进行单个预测。

    # univariate cnn example
    from numpy import array
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.layers import Flatten
    from keras.layers.convolutional import Conv1D
    from keras.layers.convolutional import MaxPooling1D
    
    # split a univariate sequence into samples
    def split_sequence(sequence, n_steps):
    	X, y = list(), list()
    	for i in range(len(sequence)):
    		# find the end of this pattern
    		end_ix = i + n_steps
    		# check if we are beyond the sequence
    		if end_ix > len(sequence)-1:
    			break
    		# gather input and output parts of the pattern
    		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
    		X.append(seq_x)
    		y.append(seq_y)
    	return array(X), array(y)
    
    # define input sequence
    raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
    # choose a number of time steps
    n_steps = 3
    # split into samples
    X, y = split_sequence(raw_seq, n_steps)
    # reshape from [samples, timesteps] into [samples, timesteps, features]
    n_features = 1
    X = X.reshape((X.shape[0], X.shape[1], n_features))
    # define model
    model = Sequential()
    model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
    model.add(MaxPooling1D(pool_size=2))
    model.add(Flatten())
    model.add(Dense(50, activation='relu'))
    model.add(Dense(1))
    model.compile(optimizer='adam', loss='mse')
    # fit model
    model.fit(X, y, epochs=1000, verbose=0)
    # demonstrate prediction
    x_input = array([70, 80, 90])
    x_input = x_input.reshape((1, n_steps, n_features))
    yhat = model.predict(x_input, verbose=0)
    print(yhat)
    

    运行示例准备数据、拟合模型并进行预测。

    注意:由于算法或计算过程的随机性,或数值精度的差异,结果可能会有所不同。考虑运行该示例几次并比较平均结果。

    我们可以看到模型预测了序列中的下一个值。

    [[101.67965]]
    

    多元CNN模型

    多元时间序列数据是指每个时间步有多个观测值的数据。

    对于多元时间序列数据,我们可能需要两种主要模型,它们是:

    1. 多输入系列。
    2. 多并联串联。

    多输入序列

    一个问题可能有两个或多个并行输入时间序列和一个依赖于输入时间序列的输出时间序列。

    输入时间序列是平行的,因为每个序列在相同的时间步长上都有观测值。

    我们可以用两个平行输入时间序列的简单例子来说明这一点,其中输出序列是输入序列的简单相加。

    # define input sequence
    in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
    in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
    out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
    

    我们可以将这三个数据数组重塑为一个数据集,其中每一行是一个时间步长,每一列是一个单独的时间序列。

    这是在CSV文件中存储并行时间序列的标准方法。

    # convert to [rows, columns] structure
    in_seq1 = in_seq1.reshape((len(in_seq1), 1))
    in_seq2 = in_seq2.reshape((len(in_seq2), 1))
    out_seq = out_seq.reshape((len(out_seq), 1))
    # horizontally stack columns
    dataset = hstack((in_seq1, in_seq2, out_seq))
    

    下面列出了完整的示例。

    # multivariate data preparation
    from numpy import array
    from numpy import hstack
    # define input sequence
    in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
    in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
    out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
    # convert to [rows, columns] structure
    in_seq1 = in_seq1.reshape((len(in_seq1), 1))
    in_seq2 = in_seq2.reshape((len(in_seq2), 1))
    out_seq = out_seq.reshape((len(out_seq), 1))
    # horizontally stack columns
    dataset = hstack((in_seq1, in_seq2, out_seq))
    print(dataset)
    

    运行该示例打印数据集,每个时间步一行,两个输入和一个输出并行时间序列各一列。

    [[ 10  15  25]
     [ 20  25  45]
     [ 30  35  65]
     [ 40  45  85]
     [ 50  55 105]
     [ 60  65 125]
     [ 70  75 145]
     [ 80  85 165]
     [ 90  95 185]]
    

    与单变量时间序列一样,我们必须用输入和输出样本将这些数据构造成样本。

    一维CNN模型需要足够的上下文来学习从输入序列到输出值的映射。CNNs可以支持作为独立通道的并行输入时间序列,如图像的红、绿、蓝分量。因此,我们需要将数据分割成样本,保持两个输入序列的观测顺序。

    如果我们选择三个输入时间步,那么第一个示例如下所示:

    输入:

    10, 15
    20, 25
    30, 35
    

    输出:

    65
    

    也就是说,每个并行序列的前三个时间步作为输入提供给模型,并且模型将其与第三个时间步(在本例中为65)的输出序列中的值相关联。

    我们可以看到,在将时间序列转换为输入/输出样本以训练模型时,我们将不得不丢弃输出时间序列中的一些值,因为在先前的时间步长中,我们在输入时间序列中没有值。反过来,输入时间步数大小的选择将对使用多少训练数据产生重要影响。

    我们可以定义一个名为split_sequences()的函数,该函数将接收一个数据集,正如我们所定义的那样,其中的行表示时间步,列表示并行序列,并返回输入/输出样本。

    # split a multivariate sequence into samples
    def split_sequences(sequences, n_steps):
    	X, y = list(), list()
    	for i in range(len(sequences)):
    		# find the end of this pattern
    		end_ix = i + n_steps
    		# check if we are beyond the dataset
    		if end_ix > len(sequences):
    			break
    		# gather input and output parts of the pattern
    		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
    		X.append(seq_x)
    		y.append(seq_y)
    	return array(X), array(y)
    

    我们可以在数据集上测试这个函数,使用每个输入时间序列的三个时间步作为输入。

    下面列出了完整的示例。

    # multivariate data preparation
    from numpy import array
    from numpy import hstack
    
    # split a multivariate sequence into samples
    def split_sequences(sequences, n_steps):
    	X, y = list(), list()
    	for i in range(len(sequences)):
    		# find the end of this pattern
    		end_ix = i + n_steps
    		# check if we are beyond the dataset
    		if end_ix > len(sequences):
    			break
    		# gather input and output parts of the pattern
    		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
    		X.append(seq_x)
    		y.append(seq_y)
    	return array(X), array(y)
    
    # define input sequence
    in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
    in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
    out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
    # convert to [rows, columns] structure
    in_seq1 = in_seq1.reshape((len(in_seq1), 1))
    in_seq2 = in_seq2.reshape((len(in_seq2), 1))
    out_seq = out_seq.reshape((len(out_seq), 1))
    # horizontally stack columns
    dataset = hstack((in_seq1, in_seq2, out_seq))
    # choose a number of time steps
    n_steps = 3
    # convert into input/output
    X, y = split_sequences(dataset, n_steps)
    print(X.shape, y.shape)
    # summarize the data
    for i in range(len(X)):
    	print(X[i], y[i])
    

    运行该示例首先打印X和y组件的形状。

    我们可以看到X分量具有三维结构。

    第一个维度是样本数,在本例中为7。第二个维度是每个样本的时间步数,在本例中是3,即为函数指定的值。最后,最后一个维度指定了并行时间序列的数量或变量的数量,在本例中,2表示两个并行序列。

    这是精确的三维结构所期望的一维CNN作为输入。数据已准备就绪,无需进一步整形即可使用。

    然后我们可以看到每个样本的输入和输出都被打印出来,显示了两个输入序列中每个序列的三个时间步以及每个样本的相关输出。

    (7, 3, 2) (7,)
    
    [[10 15]
     [20 25]
     [30 35]] 65
    [[20 25]
     [30 35]
     [40 45]] 85
    [[30 35]
     [40 45]
     [50 55]] 105
    [[40 45]
     [50 55]
     [60 65]] 125
    [[50 55]
     [60 65]
     [70 75]] 145
    [[60 65]
     [70 75]
     [80 85]] 165
    [[70 75]
     [80 85]
     [90 95]] 185
    

    我们现在准备在这个数据上拟合一个1dcnn模型,为每个输入样本指定预期的时间步数和特性,在本例中分别是3个和2个。

    # define model
    model = Sequential()
    model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
    model.add(MaxPooling1D(pool_size=2))
    model.add(Flatten())
    model.add(Dense(50, activation='relu'))
    model.add(Dense(1))
    model.compile(optimizer='adam', loss='mse')
    

    在进行预测时,该模型期望两个输入时间序列有三个时间步长。

    我们可以预测输出序列中的下一个值,提供以下输入值:

    80,	 85
    90,	 95
    100, 105
    

    具有三个时间步长和两个变量的一个样本的形状必须是[1,3,2]。

    我们希望序列中的下一个值是100+105或205。

    # demonstrate prediction
    x_input = array([[80, 85], [90, 95], [100, 105]])
    x_input = x_input.reshape((1, n_steps, n_features))
    yhat = model.predict(x_input, verbose=0)
    

    下面列出了完整的示例。

    # multivariate cnn example
    from numpy import array
    from numpy import hstack
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.layers import Flatten
    from keras.layers.convolutional import Conv1D
    from keras.layers.convolutional import MaxPooling1D
    
    # split a multivariate sequence into samples
    def split_sequences(sequences, n_steps):
    	X, y = list(), list()
    	for i in range(len(sequences)):
    		# find the end of this pattern
    		end_ix = i + n_steps
    		# check if we are beyond the dataset
    		if end_ix > len(sequences):
    			break
    		# gather input and output parts of the pattern
    		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
    		X.append(seq_x)
    		y.append(seq_y)
    	return array(X), array(y)
    
    # define input sequence
    in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
    in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
    out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
    # convert to [rows, columns] structure
    in_seq1 = in_seq1.reshape((len(in_seq1), 1))
    in_seq2 = in_seq2.reshape((len(in_seq2), 1))
    out_seq = out_seq.reshape((len(out_seq), 1))
    # horizontally stack columns
    dataset = hstack((in_seq1, in_seq2, out_seq))
    # choose a number of time steps
    n_steps = 3
    # convert into input/output
    X, y = split_sequences(dataset, n_steps)
    # the dataset knows the number of features, e.g. 2
    n_features = X.shape[2]
    # define model
    model = Sequential()
    model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
    model.add(MaxPooling1D(pool_size=2))
    model.add(Flatten())
    model.add(Dense(50, activation='relu'))
    model.add(Dense(1))
    model.compile(optimizer='adam', loss='mse')
    # fit model
    model.fit(X, y, epochs=1000, verbose=0)
    # demonstrate prediction
    x_input = array([[80, 85], [90, 95], [100, 105]])
    x_input = x_input.reshape((1, n_steps, n_features))
    yhat = model.predict(x_input, verbose=0)
    print(yhat)
    

    注意:由于算法或计算过程的随机性,或数值精度的差异,结果可能会有所不同。考虑运行该示例几次并比较平均结果。

    运行示例准备数据、拟合模型并进行预测。

    [[206.0161]]
    

    还有另一种更精细的方法来模拟这个问题。

    每个输入序列可以由一个单独的CNN来处理,并且在对输出序列进行预测之前,可以将这些子模型的输出进行组合。

    我们可以称之为一个多头CNN模型。它可以提供更多的灵活性或更好的性能,这取决于所建模问题的具体情况。例如,它允许您为每个输入序列配置不同的子模型,例如过滤器映射的数量和内核大小。

    这种类型的模型可以使用Keras函数API在Keras中定义。

    首先,我们可以将第一个输入模型定义为一个一维CNN,它的输入层要求向量具有n个步长和1个特征。

    # first input model
    visible1 = Input(shape=(n_steps, n_features))
    cnn1 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible1)
    cnn1 = MaxPooling1D(pool_size=2)(cnn1)
    cnn1 = Flatten()(cnn1)
    

    我们可以用同样的方法定义第二个输入子模型。

    # second input model
    visible2 = Input(shape=(n_steps, n_features))
    cnn2 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible2)
    cnn2 = MaxPooling1D(pool_size=2)(cnn2)
    cnn2 = Flatten()(cnn2)
    

    既然已经定义了两个输入子模型,我们就可以将每个模型的输出合并成一个长向量,在对输出序列进行预测之前可以对其进行解释。

    # merge input models
    merge = concatenate([cnn1, cnn2])
    dense = Dense(50, activation='relu')(merge)
    output = Dense(1)(dense)
    

    然后我们可以将输入和输出连接在一起。

    model = Model(inputs=[visible1, visible2], outputs=output)
    

    下图提供了该模型的外观示意图,包括每个层的输入和输出的形状。
    在这里插入图片描述此模型要求输入作为两个元素的列表提供,其中列表中的每个元素都包含其中一个子模型的数据。

    为了实现这一点,我们可以将三维输入数据分成两个独立的输入数据数组;即从一个具有[7,3,2]形状的数组到两个具有[7,3,1]形状的三维数组

    # one time series per head
    n_features = 1
    # separate input data
    X1 = X[:, :, 0].reshape(X.shape[0], X.shape[1], n_features)
    X2 = X[:, :, 1].reshape(X.shape[0], X.shape[1], n_features)
    

    然后可以提供这些数据来拟合模型。

    # fit model
    model.fit([X1, X2], y, epochs=1000, verbose=0)
    

    同样,在进行一步预测时,我们必须将单个样本的数据准备为两个独立的二维数组。

    x_input = array([[80, 85], [90, 95], [100, 105]])
    x1 = x_input[:, 0].reshape((1, n_steps, n_features))
    x2 = x_input[:, 1].reshape((1, n_steps, n_features))
    

    我们可以将所有这些联系在一起;下面列出了完整的示例。

    # multivariate multi-headed 1d cnn example
    from numpy import array
    from numpy import hstack
    from keras.models import Model
    from keras.layers import Input
    from keras.layers import Dense
    from keras.layers import Flatten
    from keras.layers.convolutional import Conv1D
    from keras.layers.convolutional import MaxPooling1D
    from keras.layers.merge import concatenate
    
    # split a multivariate sequence into samples
    def split_sequences(sequences, n_steps):
    	X, y = list(), list()
    	for i in range(len(sequences)):
    		# find the end of this pattern
    		end_ix = i + n_steps
    		# check if we are beyond the dataset
    		if end_ix > len(sequences):
    			break
    		# gather input and output parts of the pattern
    		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
    		X.append(seq_x)
    		y.append(seq_y)
    	return array(X), array(y)
    
    # define input sequence
    in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
    in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
    out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
    # convert to [rows, columns] structure
    in_seq1 = in_seq1.reshape((len(in_seq1), 1))
    in_seq2 = in_seq2.reshape((len(in_seq2), 1))
    out_seq = out_seq.reshape((len(out_seq), 1))
    # horizontally stack columns
    dataset = hstack((in_seq1, in_seq2, out_seq))
    # choose a number of time steps
    n_steps = 3
    # convert into input/output
    X, y = split_sequences(dataset, n_steps)
    # one time series per head
    n_features = 1
    # separate input data
    X1 = X[:, :, 0].reshape(X.shape[0], X.shape[1], n_features)
    X2 = X[:, :, 1].reshape(X.shape[0], X.shape[1], n_features)
    # first input model
    visible1 = Input(shape=(n_steps, n_features))
    cnn1 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible1)
    cnn1 = MaxPooling1D(pool_size=2)(cnn1)
    cnn1 = Flatten()(cnn1)
    # second input model
    visible2 = Input(shape=(n_steps, n_features))
    cnn2 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible2)
    cnn2 = MaxPooling1D(pool_size=2)(cnn2)
    cnn2 = Flatten()(cnn2)
    # merge input models
    merge = concatenate([cnn1, cnn2])
    dense = Dense(50, activation='relu')(merge)
    output = Dense(1)(dense)
    model = Model(inputs=[visible1, visible2], outputs=output)
    model.compile(optimizer='adam', loss='mse')
    # fit model
    model.fit([X1, X2], y, epochs=1000, verbose=0)
    # demonstrate prediction
    x_input = array([[80, 85], [90, 95], [100, 105]])
    x1 = x_input[:, 0].reshape((1, n_steps, n_features))
    x2 = x_input[:, 1].reshape((1, n_steps, n_features))
    yhat = model.predict([x1, x2], verbose=0)
    print(yhat)
    

    注意:由于算法或计算过程的随机性,或数值精度的差异,结果可能会有所不同。考虑运行该示例几次并比较平均结果。
    运行示例准备数据、拟合模型并进行预测。

    [[205.871]]
    
    展开全文
  • 尝试采用人工神经网络模型,针对历年招生数量原始数据信息零散、隐含影响因素过多、诸多影响因素难以确定性描述等问题,通过对黑龙江省历年研究生招生数量进行系统分析,建立了人工神经网络预测模型,并对未来3年...
  • 尝试采用人工神经网络模型,针对历年招生数量原始数据信息零散、隐含影响因素过多、诸多影响因素难以确定性描述等问题,通过对黑龙江省历年研究生招生数量进行系统分析,建立了人工神经网络预测模型,并对未来3年...
  • 机器学习:神经网络的模型构建

    千次阅读 2018-03-28 04:34:18
    简单的神经元:逻辑单元(Logistic Unit)由于神经网络建立在很多个神经元的基础上,其中每一个神经元都是一个学习模型,这些神经元叫做激活单元(Activation Unit)。以逻辑回归模型为例,采纳一些特征作为输入,给...

    什么是神经网络

    神经网络是一种模拟人脑工作原理,从而实现类人工智能的机器学习技术,支持处理图像、文本、语音以及序列多种类型的数据,可以实现分类、回归和预测等。


    简单的神经元:逻辑单元(Logistic Unit)

    由于神经网络建立在很多个神经元的基础上,其中每一个神经元都是一个学习模型,这些神经元叫做激活单元(Activation Unit)。以逻辑回归模型为例,采纳一些特征作为输入,给出逻辑输出,如下图:

    Logistic Unit

    也可以用简单的式子来表示: \left[ \begin{matrix} x_{0}\\ x_{1}\\ x_{2}\\ x_{3}\\\end{matrix}\right]\rightarrow [\space\space\space\space\space\space]\rightarrow h_{\theta}(x) .

    图中空心的圆圈就是类似神经元的东西,类似神经元的树突,这个圆圈的输入神经为它传入信息,然后它对其进行计算,再通过它的输出神经(轴突)将得到的结果信息传出。

    其中:h_{\theta}(x) = \frac{1}{1+e^{-\theta^{T}X}} (激励函数,activation function ),x=\left[ \begin{matrix} x_{0}\\ x_{1}\\ x_{2}\\ x_{3}\\\end{matrix}\right](输入), \theta=\left[ \begin{matrix} \theta_{0}\\ \theta_{1}\\ \theta_{2}\\ \theta_{3}\\\end{matrix}\right] (权重,weights).

    这就是最简单的一种模拟神经元的逻辑单元模型。我们在机器学习中的目标就是,得到最合适的权重组合,使得整个神经网络的预测效果达到最佳。

    【总结】一个神经元的组成:

    • 输入数据
    • 线性加权
    • 激励函数(非线性,易求导)
    • 输出数据

    神经网络的模型表示

    神经网络就是多个神经元组合到一起的集合,如下图:

    Neural Network

    有时第一层也要加上额外的节点 x_{0} ,第二层加一个偏执单元(bias unit)a_{0}^{(2)} 。图中 Layer 1 叫做输入层(Input Layer),Layer 3 叫做输出层(Output Layer),Layer 2 叫做隐藏层(Hidden Layer)。输入层和输出层只有一个,而隐藏层可以有多个,实际上非输入层或非输出层的层都是隐藏层。

    我们规定:a_i^{(j)} 表示第 j 层的第 i 个神经元, \Theta^{(j)} 为一个权重矩阵,控制从第 j 层到第 j+1 层的映射。在上图的神经网络中,第二层的数据可以表示为:

    a_1^{(2)}=g(\Theta_{10}^{(1)}x_0+\Theta_{11}^{(1)}x_1+\Theta_{12}^{(1)}x_2+\Theta_{13}^{(1)}x_3)
    a_2^{(2)}=g(\Theta_{20}^{(1)}x_0+\Theta_{21}^{(1)}x_1+\Theta_{22}^{(1)}x_2+\Theta_{23}^{(1)}x_3)

    a_3^{(2)}=g(\Theta_{30}^{(1)}x_0+\Theta_{31}^{(1)}x_1+\Theta_{32}^{(1)}x_2+\Theta_{33}^{(1)}x_3)

    其中的 g(x) 代表的是激励函数,也就是说这里的激励函数作用在输入数据经过权重处理后的线性组合上。参数矩阵 \Theta^{(1)} 控制了三个输入单元到三个隐藏单元的映射,因此它是一个维度为 3x4 的矩阵。

    h_{\Theta}(x)=a_{1}^{(3)}=g(\Theta_{10}^{(2)}a_{0}^{(2)}+\Theta_{11}^{(2)}a_{1}^{(2)}+\Theta_{12}^{(2)}a_{2}^{(2)}+\Theta_{13}^{(2)}a_{3}^{(2)}) .

    一般来说,如果一个神经网络在第 j 层有 s_{j} 个单元,在第 j+1 层有 s_{j+1} 个单元,那么矩阵 \Theta^{j} 控制着第 j 层到第 j+1 层的映射,它的维度为 s_{j+1}\times(s_{j}+1) . 其中第二项的 +1 来自 \Theta^{(j)} 中的偏执节点 x_{0}\Theta_{0}^{(j)} ,也就是说输入层包括偏执节点,而输出层不包括。

    接下来,再定义一些额外的项,我们把激励函数 g 括号里的部分记作 z_{i}^{(j)} ,于是上面的表达式可以写作:

    a_{1}^{(2)}=g(z_{1}^{(2)})

    a_{2}^{(2)}=g(z_{2}^{(2)})

    a_{3}^{(2)}=g(z_{3}^{(2)})

    h_{\Theta}(x)=a_{1}^{(3)}=g(z^{(3)})

    两组式子对比,我们可以看出来: z^{(2)}=\Theta^{(1)}x=a^{(1)}a^{(2)}=g(z^{(2)}) ,其中: z^{(2)}=\left[ \begin{matrix} z_{1}^{(2)}\\ z_{2}^{(2)}\\ z_{3}^{(2)}\\\end{matrix}\right] . 这里 a^{(2)}z^{(2)} 都是三维向量,因此激励函数 g 是逐个作用于 a^{(2)} 中的数据的。

    若我们增加一项 a_{0}^{(2)}=1 ,那么 z^{(3)}=\Theta^{(2)}a^{(2)}h_{\Theta}(x)=a^{(3)}=g(z^{(3)}) .

    这个计算 h(x) 的过程也叫做前向传播(forward propagation)。

    展开全文
  • 误差反向传播神经网络(Back Propagation Neural Network)模型是近年应用得最广泛网络之一,80%以上应用如函数逼近、模式识别、线性分类、数据压缩等都是基于BP神经网络模型。由于真菌定量化复杂性所致,目前...
  • 在前面的章节,讲解了赛题的背景知识和赛题数据的读取。本章开始构建一字符识别模型,基于对赛题理解本章将构建一定长字符分类模型。...CNN每一层由众多的卷积核组成,每卷积核对输入的像素

    在前面的章节,讲解了赛题的背景知识和赛题数据的读取。本章开始构建一个字符识别模型,基于对赛题理解本章将构建一个定长多字符分类模型。

    3.1 卷积神经网络-- CNN介绍

    卷积神经网络(Convolutional Neural Network, CNN)是一类特殊的人工神经网络,是深度学习中重要的一个分支。CNN在很多领域都表现优异,精度和速度比传统计算学习算法高很多。特别是在计算机视觉领域,CNN是解决图像分类、图像检索、物体检测和语义分割的主流模型。
    CNN每一层由众多的卷积核组成,每个卷积核对输入的像素进行卷积操作,得到下一次的输入。随着网络层的增加卷积核会逐渐扩大感受野,并缩减图像的尺寸。

    卷积神经网络与普通神经网络非常相似,它们都由具有可学习的权重和偏置常量的神经元组成。每个神经元都接收一些输入,并做一些点积计算,输出是每个分类的分数,普通神经网络里的一些计算技巧到这里依旧适用。

    所以哪里不同呢?卷积神经网络默认输入是图像,可以让我们把特定的性质编码入网络结构,使是我们的前馈函数更加有效率,并减少了大量参数。

    具有三维体积的神经元(3D volumes of neurons)
    卷积神经网络利用输入是图片的特点,把神经元设计成三个维度 : width, height, depth。比如输入的图片大小是 32 × 32 × 3 (rgb),那么输入神经元就也具有 32×32×3 的维度。下面是传统神经网络的示意图:
    在这里插入图片描述
    一个卷积神经网络由很多层组成,它们的输入是三维的,输出也是三维的,有的层有参数,有的层不需要参数。卷积神经网络的示意图如下:
    在这里插入图片描述
    卷积神经网络通常包含以下几种层:
    1、卷积层(Convolutional layer)

    卷积神经网路中每层卷积层由若干卷积单元组成,每个卷积单元的参数都是通过反向传播算法优化得到的。卷积运算的目的是提取输入的不同特征,第一层卷积层可能只能提取一些低级的特征如边缘、线条和角等层级,更多层的网络能从低级特征中迭代提取更复杂的特征。

    在这里插入图片描述

    2、池化层(Pooling layer)

    通常在卷积层之后会得到维度很大的特征,将特征切成几个区域,取其最大值或平均值,得到新的、维度较小的特征。

    池化即下采样,目的是为了减少特征图。池化操作对每个深度切片独立,规模一般为 2*2,相对于卷积层进行卷积运算,池化层进行的运算一般有以下几种:

    • 最大池化(Max Pooling)。取4个点的最大值。这是最常用的池化方法。
    • 均值池化(Mean Pooling)。取4个点的均值。
    • 高斯池化。借鉴高斯模糊的方法。不常用。
    • 可训练池化。训练函数 ff ,接受4个点为输入,出入1个点。不常用。

    最常见的池化层是规模为2*2, 步幅为2,对输入的每个深度切片进行下采样。每个MAX操作对四个数进行,如下图所示:
    在这里插入图片描述
    池化操作将保存深度大小不变。如果池化层的输入单元大小不是二的整数倍,一般采取边缘补零(zero-padding)的方式补成2的倍数,然后再池化。

    3、非线性激活函数(non-linear activation function)

    神经的非线性激活化函数,用于增加网络的非线性分割能力,一般用Relu函数。

    4、全连接层( Fully-Connected layer)

    完全连接层是一个传统的多层感知器,它在输出层使用 softmax 激活函数。把所有局部特征结合变成全局特征,用来计算最后每一类的得分。

    一个卷积神经网络各层应用实例:
    在这里插入图片描述

    3.2 LeNet

    手写字体识别模型LeNet5诞生于1994年,是最早的卷积神经网络之一。LeNet5通过巧妙的设计,利用卷积、参数共享、池化等操作提取特征,避免了大量的计算成本,最后再使用全连接神经网络进行分类识别,这个网络也是最近大量神经网络架构的起点。

    如下图所示为LeNet网络结构,总共有7层网络(不含输入层),2个卷积层、2个池化层、3个全连接层。
    在这里插入图片描述

    LeNet分为卷积层块和全连接层块两个部分。下面我们分别介绍这两个模块。卷积层块里的基本单位是卷积层后接最大池化层:卷积层用来识别图像里的空间模式,如线条和物体局部,之后的最大池化层则用来降低卷积层对位置的敏感性。卷积层块由两个这样的基本单位重复堆叠构成。在卷积层块中,每个卷积层都使用5×55\times 5的窗口,并在输出上使用sigmoid激活函数。第一个卷积层输出通道数为6,第二个卷积层输出通道数则增加到16。这是因为第二个卷积层比第一个卷积层的输入的高和宽要小,所以增加输出通道使两个卷积层的参数尺寸类似。卷积层块的两个最大池化层的窗口形状均为2×22\times 2,且步幅为2。由于池化窗口与步幅形状相同,池化窗口在输入上每次滑动所覆盖的区域互不重叠。
    卷积层块的输出形状为(批量大小, 通道, 高, 宽)。当卷积层块的输出传入全连接层块时,全连接层块会将小批量中每个样本变平(flatten)。也就是说,全连接层的输入形状将变成二维,其中第一维是小批量中的样本,第二维是每个样本变平后的向量表示,且向量长度为通道、高和宽的乘积。全连接层块含3个全连接层。它们的输出个数分别是120、84和10,其中10为输出的类别个数。
    在卷积层块中输入的高和宽在逐层减小。卷积层由于使用高和宽均为5的卷积核,从而将高和宽分别减小4,而池化层则将高和宽减半,但通道数则从1增加到16。全连接层则逐层减少输出个数,直到变成图像的类别数10。
    在这里插入图片描述
    通过多次卷积和池化,CNN的最后一层将输入的图像像素映射为具体的输出。如在分类任务中会转换为不同类别的概率输出,然后计算真实标签与CNN模型的预测结果的差异,并通过反向传播更新每层的参数,并在更新完成后再次前向传播,如此反复直到训练完成 。

    一个数字识别的效果如图所示:
    在这里插入图片描述

    3.3 卷积神经网络进阶

    随着网络结构的发展,研究人员最初发现网络模型结构越深、网络参数越多模型的精度更优。比较典型的是AlexNet、VGG、InceptionV3和ResNet的发展脉络。
    在这里插入图片描述

    1、AlexNet(2012)

    2012年,AlexNet横空出世。这个模型的名字来源于论文第一作者的姓名Alex Krizhevsky。AlexNet使用了8层卷积神经网络,并以很大的优势赢得了ImageNet 2012图像识别挑战赛。它首次证明了学习到的特征可以超越手工设计的特征,从而一举打破计算机视觉研究的前状。
    在这里插入图片描述
    AlexNet与LeNet的设计理念非常相似,但也有显著的区别。

    • 1、与相对较小的LeNet相比,AlexNet包含8层变换,其中有5层卷积和2层全连接隐藏层,以及1个全连接输出层。下面我们来详细描述这些层的设计。
      AlexNet第一层中的卷积窗口形状是11×1111\times11。因为ImageNet中绝大多数图像的高和宽均比MNIST图像的高和宽大10倍以上,ImageNet图像的物体占用更多的像素,所以需要更大的卷积窗口来捕获物体。第二层中的卷积窗口形状减小到5×55\times5,之后全采用3×33\times3。此外,第一、第二和第五个卷积层之后都使用了窗口形状为3×33\times3、步幅为2的最大池化层。而且,AlexNet使用的卷积通道数也大于LeNet中的卷积通道数数十倍。
      紧接着最后一个卷积层的是两个输出个数为4096的全连接层。这两个巨大的全连接层带来将近1 GB的模型参数。由于早期显存的限制,最早的AlexNet使用双数据流的设计使一个GPU只需要处理一半模型。幸运的是,显存在过去几年得到了长足的发展,因此通常我们不再需要这样的特别设计了。
    • 2、AlexNet将sigmoid激活函数改成了更加简单的ReLU激活函数。一方面,ReLU激活函数的计算更简单,例如它并没有sigmoid激活函数中的求幂运算。另一方面,ReLU激活函数在不同的参数初始化方法下使模型更容易训练。这是由于当sigmoid激活函数输出极接近0或1时,这些区域的梯度几乎为0,从而造成反向传播无法继续更新部分模型参数;而ReLU激活函数在正区间的梯度恒为1。因此,若模型参数初始化不当,sigmoid函数可能在正区间得到几乎为0的梯度,从而令模型无法得到有效训练。
    • 3、AlexNet通过丢弃法来控制全连接层的模型复杂度。而LeNet并没有使用丢弃法。
    • 4、AlexNet引入了大量的图像增广,如翻转、裁剪和颜色变化,从而进一步扩大数据集来缓解过拟合。我们将在后面的图像增广中详细介绍这种方法。

    小结:

    • AlexNet跟LeNet结构类似,但使用了更多的卷积层和更大的参数空间来拟合大规模数据集ImageNet。它是浅层神经网络和深度神经网络的分界线。
    • 虽然看上去AlexNet的实现比LeNet的实现也就多了几行代码而已,但这个观念上的转变和真正优秀实验结果的产生令学术界付出了很多年。

    2、VGG-16(2014)

    AlexNet在LeNet的基础上增加了3个卷积层。但AlexNet作者对它们的卷积窗口、输出通道数和构造顺序均做了大量的调整。虽然AlexNet指明了深度卷积神经网络可以取得出色的结果,但并没有提供简单的规则以指导后来的研究者如何设计新的网络。
    VGG,它的名字来源于论文作者所在的实验室Visual Geometry Group。VGG提出了可以通过重复使用简单的基础块来构建深度模型的思路。
    VGG的结构图如下:
    在这里插入图片描述
    - - - VGG块

    VGG块的组成规律是:连续使用数个相同的填充为1、窗口形状为3×33\times 3的卷积层后接上一个步幅为2、窗口形状为2×22\times 2的最大池化层。卷积层保持输入的高和宽不变,而池化层则对其减半。我们使用vgg_block函数来实现这个基础的VGG块,它可以指定卷积层的数量和输入输出通道数。

    对于给定的感受野(与输出有关的输入图片的局部大小),采用堆积的小卷积核优于采用大的卷积核,因为可以增加网络深度来保证学习更复杂的模式,而且代价还比较小(参数更少)。例如,在VGG中,使用了3个3x3卷积核来代替7x7卷积核,使用了2个3x3卷积核来代替5*5卷积核,这样做的主要目的是在保证具有相同感知野的条件下,提升了网络的深度,在一定程度上提升了神经网络的效果。

    与AlexNet和LeNet一样,VGG网络由卷积层模块后接全连接层模块构成。卷积层模块串联数个vgg_block,其超参数由变量conv_arch定义。该变量指定了每个VGG块里卷积层个数和输入输出通道数。全连接模块则跟AlexNet中的一样。
    现在我们构造一个VGG网络。它有5个卷积块,前2块使用单卷积层,而后3块使用双卷积层。第一块的输入输出通道分别是1(因为下面要使用的Fashion-MNIST数据的通道数为1)和64,之后每次对输出通道数翻倍,直到变为512。因为这个网络使用了8个卷积层和3个全连接层,所以经常被称为VGG-11。
    可以看到,每次我们将输入的高和宽减半,直到最终高和宽变成7后传入全连接层。与此同时,输出通道数每次翻倍,直到变成512。因为每个卷积层的窗口大小一样,所以每层的模型参数尺寸和计算复杂度与输入高、输入宽、输入通道数和输出通道数的乘积成正比。VGG这种高和宽减半以及通道翻倍的设计使得多数卷积层都有相同的模型参数尺寸和计算复杂度。
    VGG:通过重复使⽤简单的基础块来构建深度模型。
    Block: 数个相同的填充为1、窗口形状为3×3的卷积层,接上一个步幅为2、窗口形状为2×2的最大池化层。卷积层保持输入的高和宽不变,而池化层则对其减半。
    VGG和AlexNet的网络图对比如下:
    在这里插入图片描述

    在这里插入图片描述
    小结:
    VGG-11通过5个可以重复使用的卷积块来构造网络。根据每块里卷积层个数和输出通道数的不同可以定义出不同的VGG模型。

    3、网络中的网络(NiN)

    LeNet、AlexNet和VGG:先以由卷积层构成的模块充分抽取空间特征,再以由全连接层构成的模块来输出分类结果。
    NiN:串联多个由卷积层和“全连接”层构成的小⽹络来构建⼀个深层⽹络。
    ⽤了输出通道数等于标签类别数的NiN块,然后使⽤全局平均池化层对每个通道中所有元素求平均并直接用于分类。
    在这里插入图片描述
    - - - 1×1卷积核作用

    • 放缩通道数:通过控制卷积核的数量达到通道数的放缩。
    • 增加非线性。1×1卷积核的卷积过程相当于全连接层的计算过程,并且还加入了非线性激活函数,从而可以增加网络的非线性。
    • 计算参数少

    - - - NiN块
    我们知道,卷积层的输入和输出通常是四维数组(样本,通道,高,宽),而全连接层的输入和输出则通常是二维数组(样本,特征)。如果想在全连接层后再接上卷积层,则需要将全连接层的输出变换为四维。回忆在多输入通道和多输出通道里介绍的1×11\times 1卷积层。它可以看成全连接层,其中空间维度(高和宽)上的每个元素相当于样本,通道相当于特征。因此,NiN使用1×11\times 1卷积层来替代全连接层,从而使空间信息能够自然传递到后面的层中去。
    NiN块是NiN中的基础块。它由一个卷积层加两个充当全连接层的1×11\times 1卷积层串联而成。其中第一个卷积层的超参数可以自行设置,而第二和第三个卷积层的超参数一般是固定的。
    NiN是在AlexNet问世不久后提出的。它们的卷积层设定有类似之处。NiN使用卷积窗口形状分别为11×1111\times 115×55\times 53×33\times 3的卷积层,相应的输出通道数也与AlexNet中的一致。每个NiN块后接一个步幅为2、窗口形状为3×33\times 3的最大池化层。
    除使用NiN块以外,NiN还有一个设计与AlexNet显著不同:NiN去掉了AlexNet最后的3个全连接层,取而代之地,NiN使用了输出通道数等于标签类别数的NiN块,然后使用全局平均池化层对每个通道中所有元素求平均并直接用于分类。这里的全局平均池化层即窗口形状等于输入空间维形状的平均池化层。NiN的这个设计的好处是可以显著减小模型参数尺寸,从而缓解过拟合。然而,该设计有时会造成获得有效模型的训练时间的增加。

    小结:

    • NiN重复使用由卷积层和代替全连接层的1×11\times 1卷积层构成的NiN块来构建深层网络。
    • NiN去除了容易造成过拟合的全连接输出层,而是将其替换成输出通道数等于标签类别数的NiN块和全局平均池化层。
    • NiN的以上设计思想影响了后面一系列卷积神经网络的设计。

    4、含并行连结的网络(GoogLeNet)

    在2014年的ImageNet图像识别挑战赛中,一个名叫GoogLeNet的网络结构大放异彩。它虽然在名字上向LeNet致敬,但在网络结构上已经很难看到LeNet的影子。GoogLeNet吸收了NiN中网络串联网络的思想,并在此基础上做了很大改进。在随后的几年里,研究人员对GoogLeNet进行了数次改进,本节将介绍这个模型系列的第一个版本。

    • 由Inception基础块组成。
    • Inception块相当于⼀个有4条线路的⼦⽹络。它通过不同窗口形状的卷积层和最⼤池化层来并⾏抽取信息,并使⽤1×1卷积层减少通道数从而降低模型复杂度。
    • 可以⾃定义的超参数是每个层的输出通道数,我们以此来控制模型复杂度。

    - - - Inception块
    GoogLeNet中的基础卷积块叫作Inception块,得名于同名电影《盗梦空间》(Inception)。与上一节介绍的NiN块相比,这个基础块在结构上更加复杂。
    在这里插入图片描述
    Inception块里有4条并行的线路。前3条线路使用窗口大小分别是1×11\times 13×33\times 35×55\times 5的卷积层来抽取不同空间尺寸下的信息,其中中间2个线路会对输入先做1×11\times 1卷积来减少输入通道数,以降低模型复杂度。第四条线路则使用3×33\times 3最大池化层,后接1×11\times 1卷积层来改变通道数。4条线路都使用了合适的填充来使输入与输出的高和宽一致。最后我们将每条线路的输出在通道维上连结,并输入接下来的层中去。
    Inception块中可以自定义的超参数是每个层的输出通道数,我们以此来控制模型复杂度。
    GoogLeNet跟VGG一样,在主体卷积部分中使用5个模块(block),每个模块之间使用步幅为2的3×33\times 3最大池化层来减小输出高宽。

    • 第一模块使用一个64通道的7×77\times 7卷积层。
    • 第二模块使用2个卷积层:首先是64通道的1×11\times 1卷积层,然后是将通道增大3倍的3×33\times 3卷积层。它对应Inception块中的第二条线路。
    • 第三模块串联2个完整的Inception块。第一个Inception块的输出通道数为64+128+32+32=25664+128+32+32=256,其中4条线路的输出通道数比例为64:128:32:32=2:4:1:164:128:32:32=2:4:1:1。其中第二、第三条线路先分别将输入通道数减小至96/192=1/296/192=1/216/192=1/1216/192=1/12后,再接上第二层卷积层。第二个Inception块输出通道数增至128+192+96+64=480128+192+96+64=480,每条线路的输出通道数之比为128:192:96:64=4:6:3:2128:192:96:64 = 4:6:3:2。其中第二、第三条线路先分别将输入通道数减小至128/256=1/2128/256=1/232/256=1/832/256=1/8
    • 第四模块更加复杂。它串联了5个Inception块,其输出通道数分别是192+208+48+64=512192+208+48+64=512160+224+64+64=512160+224+64+64=512128+256+64+64=512128+256+64+64=512112+288+64+64=528112+288+64+64=528256+320+128+128=832256+320+128+128=832。这些线路的通道数分配和第三模块中的类似,首先含3×33\times 3卷积层的第二条线路输出最多通道,其次是仅含1×11\times 1卷积层的第一条线路,之后是含5×55\times 5卷积层的第三条线路和含3×33\times 3最大池化层的第四条线路。其中第二、第三条线路都会先按比例减小通道数。这些比例在各个Inception块中都略有不同。
    • 第五模块有输出通道数为256+320+128+128=832256+320+128+128=832384+384+128+128=1024384+384+128+128=1024的两个Inception块。其中每条线路的通道数的分配思路和第三、第四模块中的一致,只是在具体数值上有所不同。需要注意的是,第五模块的后面紧跟输出层,该模块同NiN一样使用全局平均池化层来将每个通道的高和宽变成1。最后我们将输出变成二维数组后接上一个输出个数为标签类别数的全连接层。
      GoogLeNet模型的计算复杂,而且不如VGG那样便于修改通道数。
      在这里插入图片描述
      小结:
    • Inception块相当于一个有4条线路的子网络。它通过不同窗口形状的卷积层和最大池化层来并行抽取信息,并使用1×11\times 1卷积层减少通道数从而降低模型复杂度。
    • GoogLeNet将多个设计精细的Inception块和其他层串联起来。其中Inception块的通道数分配之比是在ImageNet数据集上通过大量的实验得来的。
    • GoogLeNet和它的后继者们一度是ImageNet上最高效的模型之一:在类似的测试精度下,它们的计算复杂度往往更低。

    5、残差网络(ResNet-50)

    深度学习的问题:深度CNN网络达到一定深度后再一味地增加层数并不能带来进一步地分类性能提高,反而会招致网络收敛变得更慢,准确率也变得更差。
    - - -残差块(Residual Block)
    恒等映射:
    左边:f(x)=x
    右边:f(x)-x=0 (易于捕捉恒等映射的细微波动)
    在这里插入图片描述
    ResNet沿用了VGG全3×33\times 3卷积层的设计。残差块里首先有2个有相同输出通道数的3×33\times 3卷积层。每个卷积层后接一个批量归一化层和ReLU激活函数。然后我们将输入跳过这两个卷积运算后直接加在最后的ReLU激活函数前。这样的设计要求两个卷积层的输出与输入形状一样,从而可以相加。如果想改变通道数,就需要引入一个额外的1×11\times 1卷积层来将输入变换成需要的形状后再做相加运算。在残差块中,输⼊可通过跨层的数据线路更快地向前传播。

    ResNet的前两层跟之前介绍的GoogLeNet中的一样:在输出通道数为64、步幅为2的7×77\times 7卷积层后接步幅为2的3×33\times 3的最大池化层。不同之处在于ResNet每个卷积层后增加的批量归一化层。
    ResNet-50网络结构如下:
    在这里插入图片描述

    GoogLeNet在后面接了4个由Inception块组成的模块。ResNet则使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。第一个模块的通道数同输入通道数一致。由于之前已经使用了步幅为2的最大池化层,所以无须减小高和宽。之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并将高和宽减半。

    小结:

    • 残差块通过跨层的数据通道从而能够训练出有效的深度神经网络。
    • ResNet深刻影响了后来的深度神经网络的设计。

    3.4 Pytorch构建CNN模型

    在Pytorch中构建CNN模型非常简单,只需要定义好模型的参数和正向传播即可,Pytorch会根据正向传播自动计算反向传播。
    在本章会构建一个非常简单的CNN,然后进行训练。这个CNN模型包括两个卷积层,最后并联6个全连接层进行分类,完成字符识别功能。

    import torch
    torch.manual_seed(0)
    torch.backends.cudnn.deterministic = False
    torch.backends.cudnn.benchmark = True
    
    import torchvision.models as models
    import torchvision.transforms as transforms
    import torchvision.datasets as datasets
    import torch.nn as nn
    import torch.nn.functional as F
    import torch.optim as optim
    from torch.autograd import Variable
    from torch.utils.data.dataset import Dataset
    
    # 定义模型
    class SVHN_Model1(nn.Module):
        def __init__(self):
            super(SVHN_Model1, self).__init__()
            # CNN提取特征模块
            self.cnn = nn.Sequential(
                nn.Conv2d(3, 16, kernel_size=(3, 3), stride=(2, 2)),
                nn.ReLU(),  
                nn.MaxPool2d(2),
                nn.Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2)),
                nn.ReLU(), 
                nn.MaxPool2d(2),
            )
            # 
            self.fc1 = nn.Linear(32*3*7, 11)
            self.fc2 = nn.Linear(32*3*7, 11)
            self.fc3 = nn.Linear(32*3*7, 11)
            self.fc4 = nn.Linear(32*3*7, 11)
            self.fc5 = nn.Linear(32*3*7, 11)
            self.fc6 = nn.Linear(32*3*7, 11)    
            
        def forward(self, img):        
            feat = self.cnn(img)
            feat = feat.view(feat.shape[0], -1)
            c1 = self.fc1(feat)
            c2 = self.fc2(feat)
            c3 = self.fc3(feat)
            c4 = self.fc4(feat)
            c5 = self.fc5(feat)
            c6 = self.fc6(feat)
            return c1, c2, c3, c4, c5, c6    
            
    model = SVHN_Model1()

    接下来是训练代码:

    # 损失函数
    criterion = nn.CrossEntropyLoss()
    # 优化器
    optimizer = torch.optim.Adam(model.parameters(), 0.005)
    
    loss_plot, c0_plot = [], []
    # 迭代10个Epoch
    for epoch in range(10):
        for data in train_loader:
            c0, c1, c2, c3, c4, c5 = model(data[0])
            loss = criterion(c0, data[1][:, 0]) + \
                    criterion(c1, data[1][:, 1]) + \
                    criterion(c2, data[1][:, 2]) + \
                    criterion(c3, data[1][:, 3]) + \
                    criterion(c4, data[1][:, 4]) + \
                    criterion(c5, data[1][:, 5])
            loss /= 6
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()      
            loss_plot.append(loss.item())
            c0_plot.append((c0.argmax(1) == data[1][:, 0]).sum().item()*1.0 / c0.shape[0])        
            
        print(epoch)

    在这里插入图片描述

    为了追求精度,也可以使用在ImageNet数据集上的预训练模型,具体方法如下:

    class SVHN_Model2(nn.Module):
        def __init__(self):
            super(SVHN_Model1, self).__init__()  
            
            model_conv = models.resnet18(pretrained=True)
            model_conv.avgpool = nn.AdaptiveAvgPool2d(1)
            model_conv = nn.Sequential(*list(model_conv.children())[:-1])
            self.cnn = model_conv     
            self.fc1 = nn.Linear(512, 11)
            self.fc2 = nn.Linear(512, 11)
            self.fc3 = nn.Linear(512, 11)
            self.fc4 = nn.Linear(512, 11)
            self.fc5 = nn.Linear(512, 11)    
            
        def forward(self, img):        
            feat = self.cnn(img)
            # print(feat.shape)
            feat = feat.view(feat.shape[0], -1)
            c1 = self.fc1(feat)
            c2 = self.fc2(feat)
            c3 = self.fc3(feat)
            c4 = self.fc4(feat)
            c5 = self.fc5(feat)
            return c1, c2, c3, c4, c5

    参考

    计算机视觉实践(街景字符编码识别)
    datawhalechina
    动手学深度学习
    CNN可视化
    Learning Convolutional Neural Networks with Interactive Visualization

    Datawhale是一个专注于数据科学与AI领域的开源组织,汇集了众多领域院校和知名企业的优秀学习者,聚合了一群有开源精神和探索精神的团队成员。Datawhale以“for the learner,和学习者一起成长”为愿景,鼓励真实地展现自我、开放包容、互信互助、敢于试错和勇于担当。同时Datawhale 用开源的理念去探索开源内容、开源学习和开源方案,赋能人才培养,助力人才成长,建立起人与人,人与知识,人与企业和人与未来的联结。

    展开全文
  • 研究了BP神经网络的原理和方法,收集了全国100对矿井实测初始释放瓦斯膨胀能资料,获得20组可靠数据,选取煤破坏类型、煤层相对瓦斯压力、煤坚固性系数、瓦斯放散初速度4单项指标为BP神经网络的输入变量,...
  • 前面总结了利用单神经元和Logistic回归解决简单二分类问题,接下来就是建立简单BP神经网络模型了,也就是简单的输入层-隐藏层-输出层网络模型,相比起之前模型仅仅了一隐藏层。   先给出直观网络...

    前面总结了利用单神经元和Logistic回归解决简单的二分类问题,接下来就是建立一个简单的BP神经网络模型了,也就是简单的输入层-隐藏层-输出层网络模型,相比起之前的模型仅仅多了一个隐藏层。
      先给出直观的网络示意图(注:图来源于吴恩达老师深度学习课程JupterNotebook):

    模型解释

      输入层 x=[x1,x2,...,xn]T\vec{x}=[x_1, x_2,...,x_n]^T 对应样本的 nn 个特征。输入层到隐层的权重矩阵用 W[1]\bold{W}^{[1]} 表示,隐层的输入用 z[1]\vec{z}^{[1]} 表示,隐层的输出用 a[1]\vec{a}^{[1]} 表示。隐层到输出层的权重矩阵用 W[2]\bold{W}^{[2]} 表示,输出层的输入用 z[2]z^{[2]} 表示,网络的输出用 a[2]a^{[2]} 表示。
      从上图可以看出,对于一个二分类网络来说,输出层只需要一个神经元,用来输出一个位于 [0,1][0,1] 区间的概率值。这样对于一个输入样本 x\vec{x} ,一个网络的正向传播机制可以用数学公式描述为
    (1)z[1]=W[1]x+b[1]\vec{z}^{[1]}=\bold{W}^{[1]}\vec{x}+\vec{b}^{[1]}\tag{1}(2)a[1]=tanh(z[1])\vec{a}^{[1]}=tanh(\vec{z}^{[1]})\tag{2}(3)z[2]=W[2]a[1]+b[2]z^{[2]}=\bold{W}^{[2]}\vec{a}^{[1]}+b^{[2]}\tag{3}(4)y^=a[2]=sigmoid(z[2])\hat{y}=a^{[2]}=sigmoid(z^{[2]})\tag{4}其中 W[1]Rnh×nx\bold{W}^{[1]}\subseteq \bold{R}^{n_h\times{n_x}}W[2]Rny×nh\bold{W}^{[2]}\subseteq \bold{R}^{n_y\times{n_h}}nxnhnyn_x、n_h、n_y 分别为输入层、隐层和输出层节点数量,b[1]\vec{b}^{[1]}b[2]b^{[2]} 分别表示隐层和输出层(更确切地应该说输出神经元)的阈值,从式子里可以看出隐层用了 tanhtanh 作为激活函数,输出层毫无疑问还是 sigmoid 函数。
    这样输入样本 x\vec{x} 的预测值即为
    (5)yprediction={1,a[2]>0.50,otherwise y_{prediction}= \begin{cases}1, & a^{[2]} > 0.5 \\ 0, & \text{otherwise } \end{cases}\tag{5}

     

    多个输入样本的向量化实现

    上面用单个模型解释了一个单隐层神经网络的正向传播机制,实际中对于一个包含很多训练样本的数据集,如果通过遍历的方式一个一个样本输入神经网络进行运算是很耗时的,这时候可以尝试使用Python强大的 numpy 库进行向量化编程,numpy库中定义了各种矩阵和向量运算的函数,可以有效地提高效率。(实质上式 (1)(1)(3)(3) 就是向量化的实现。)

    • 输入样本向量化

      对于一个包含 mm 样本的数据集,我们可以将其表示为
    (6)X=[x(1),x(2),...,x(m)]\bold{X}=[\vec{x}^{(1)},\vec{x}^{(2)},...,\vec{x}^{(m)}] \tag{6}其中 x(i)=[x1(i),x2(i),...,xn(i)]T\vec{x}^{(i)}=[x^{(i)}_1, x^{(i)}_2,...,x^{(i)}_n]^T 为第 ii 个样本的特征向量,将式 (6)(6) 展开为矩阵形式即为
    X=[x1(1)x1(2)x1(m)x2(1)x2(2)x2(m)xn(1)xn(2)xn(m)] \bold{X}= \begin{bmatrix} x^{(1)}_1 & x^{(2)}_1 & \cdots & x^{(m)}_1 \\ x^{(1)}_2 & x^{(2)}_2 & \cdots & x^{(m)}_2 \\ \vdots & \vdots & \ddots & \vdots \\ x^{(1)}_n & x^{(2)}_n & \cdots & x^{(m)}_n \end{bmatrix} 显然 XRn×m\bold{X} \subseteq \bold{R}^{n\times{m}}nn为样本特征数量,同样也是网络输入层节点数量,因此 n=nxn=n_x

    • 正向传播过程

      这样就可以通过向量化编程同时将所有样本输入到网络,此时式(1)(1)-(4)(4)就可以向量化表示为以下形式:
    (7)Z[1]=W[1]X+b[1]\bold{Z}^{[1]}=\bold{W}^{[1]}\bold{X}+\bold{b}^{[1]}\tag{7}(8)A[1]=tanh(Z[1])\bold{A}^{[1]}=tanh(\bold{Z}^{[1]})\tag{8}(9)Z[2]=W[2]A[1]+b[2]\bold{Z}^{[2]}=\bold{W}^{[2]}\bold{A}^{[1]}+\bold{b}^{[2]}\tag{9}(10)Y^=A[2]=sigmoid(Z[2])\hat{\bold{Y}}=\bold{A}^{[2]}=sigmoid(\bold{Z}^{[2]})\tag{10}
    对应输出了 mm个样本的预测值 Y^=[y^(1),y^(2),...,y^(m)]\hat{\bold{Y}}=[\hat{y}^{(1)},\hat{y}^{(2)},...,\hat{y}^{(m)}]

    • 损失函数

      损失函数仍旧使用交叉熵函数,向量化表示为:
    (11)J=1m(Ylog(Y^T)+(1Y)log(1Y^)T)J=-\frac1m(\bold{Y}\log(\hat{\bold{Y}}^T)+(1-\bold{Y})\log(1-\hat{\bold{Y}})^T)\tag{11}

    • 反向传播过程

      反向传播的过程主要就是逐层对误差函数求偏导,过程和单神经元Logistic分类器类似,首先对输出层求导,接下来对隐层求导。每一步的求导结果如下:
    (12)dZ[2]=A[2]Yd\bold{Z}^{[2]}=\bold{A}^{[2]}-\bold{Y}\tag{12} (13)dW[2]=1mdZ[2]A[1]Td\bold{W}^{[2]}=\frac1md\bold{Z}^{[2]}\bold{A}^{[1]T}\tag{13}

    (14)db[2]=1mdZ[2]d\bold{b}^{[2]}=\frac{1}{m}\sum d\bold{Z}^{[2]} \tag{14}

    (15)dZ[1]=W[2]TdZ[2]g(Z[1])d\bold{Z}^{[1]}=\bold{W}^{[2]T}d\bold{Z}^{[2]}*g'(\bold{Z}^{[1]})\tag{15} (16)dW[1]=1mdZ[1]XTd\bold{W}^{[1]}=\frac1md\bold{Z}^{[1]}\bold{X}^T\tag{16}

    (17)db[1]=1mdZ[1]d\bold{b}^{[1]}=\frac{1}{m}\sum d\bold{Z}^{[1]} \tag{17}
    (15)(15)g()g'(·) 表示隐层激活函数(即 tanhtanh )的导函数,在激活函数总结中已经提到 tanhtanh 函数的导函数性质:
    (18)g(z)=1g2(z) g'(z)=1-g^2(z)\tag{18}这样就可以很容易得到
    (19)dZ[1]=W[2]TdZ[2]g(Z[1])=W[2]TdZ[2](1(A[1])2)d\bold{Z}^{[1]}=\bold{W}^{[2]T}d\bold{Z}^{[2]}*g'(\bold{Z}^{[1]})=\bold{W}^{[2]T}d\bold{Z}^{[2]}*(1-(\bold{A}^{[1]})^2)\tag{19}

    • 参数更新

      到这里基本已经完成了大部分的计算任务,最后就是利用计算得到的梯度信息更新网络的参数,即两个权重矩阵和两个阈值向量:

    (20)dW[1]=W[1]αdW[1]d\bold{W}^{[1]}=\bold{W}^{[1]}-\alpha d\bold{W}^{[1]}\tag{20}

    (21)db[1]=b[1]αdb[1]d\bold{b}^{[1]}=\bold{b}^{[1]}-\alpha d\bold{b}^{[1]}\tag{21}

    (22)dW[2]=W[2]αdW[2]d\bold{W}^{[2]}=\bold{W}^{[2]}-\alpha d\bold{W}^{[2]}\tag{22}

    (23)db[2]=b[2]αdb[2]d\bold{b}^{[2]}=\bold{b}^{[2]}-\alpha d\bold{b}^{[2]}\tag{23}
      这样在迭代次数内不断重复上面的更新过程从而不断降低损失函数的值,最终就可以得到一个较好的二分类神经网络模型。

    总结

    包含单隐层的神经网络只是一个很简单很基础的模型,实现过程中发现当隐层节点数为5的时候,最后的在测试集上的准确率甚至不如单神经元Logistic分类器;增加网络节点数会提高性能,但是计算量也明显上升。

    Python实现的代码可以在我的GitHub上下载。

    展开全文
  • 而且理论上,一三层的神经网络,能够以任意精度逼近给定的函数,这是非常诱人的期望; ②网络能通过学习带正确答案的实例集自动提取“合理的”求解规则,即具有自学习能力; ③网络具有一定的推广、概括能力。 ...
  • matlab神经网络30案例分析

    热门讨论 2011-06-01 20:06:07
    现对于一城市数量为10TSP问题,要求设计一可以对其进行组合优化连续型Hopfield神经网络模型,利用该模型可以快速地找到最优(或近似最优)一条路线。 第12章 SVM数据分类预测——意大利葡萄酒种类识别...
  • 对人脑神经元网络系统进行抽象建立模型构成的人工神经网络,简称神经网络或类神经网络神经网络的基础是大量的称为节点的神经元的元素的集合,通常以紧密互连的束状排列。简而言之,神经元是具有多个输入和单个输出...
  • 误差反向传播神经网络(Back Propagation Neural Network)模型是近年应用得最广泛网络之一,80%以上应用如函数逼近、模式识别、线性分类、数据压缩等都是基于BP神经网络模型。由于真菌定量化复杂性所致,目前...
  • 基于神经网络的故障诊断不需要建立精确数学模型,可以处理非线性问题,具有并行计算能力,不需要诊断和推理规则,它通过一组样本的输入与输出之间映射关系进行按照设定准则可以自学习。运用于故障诊断中一般...
  • 误差反向传播神经网络(Back Propagation Neural Network)模型是近年应用得最广泛网络之一,80%以上应用如函数逼近、模式识别、线性分类、数据压缩等都是基于BP神经网络模型。由于真菌定量化复杂性所致,目前...
  • 首先将Zadeh模糊推理神经网络变为直觉模糊推理网络,建立个多输入单输出T-S型ANIFIS模型;然后设计了系统变量属性函数和推理规则,确定了各层的输入输出计算关系,以及系统输出结果合成计算表达式;最后通过证明...
  • 神经网络

    2016-05-05 16:52:42
    模仿大脑神经工作方式(由多个树突接受外界环境刺激,然后将信息传到神经元进行处理,最后输出相应处理信息)建立的数学模型。x1,x2,x3 代表输入信息,黄圈代表神经元,h代表输出信息。 进一步将...
  • 网络中包含多个隐藏层,隐藏层中隐藏变量通常是二进制数,用来对输入信号进行特征提取。输入信号从深度信念网络的最底层输入,并自下而上有向地传递给隐藏层。而在网络最上面两层中,神经元之间连接是没有方向...
  • 多层感知机(MLP,Multilayer Perceptron)也叫人工神经网络(ANN,Artificial Neural Network),除了输入输出层,它中间可以有多个隐层,最简单MLP只含一个隐层,即三层结构,如下图: 对一些标记进行定义: ...
  • 利用灰色理论灰色关联分析法对选取8影响煤层瓦斯含量地质因素进行了分析,筛选出断距、埋深、基岩厚度以及挥发分4主要影响因素,并将其作为BP神经网络模型的输入建立了煤层瓦斯含量预测模型。对该预测模型...
  • 文章目录神经网络模型表示代价函数反向传播 神经网络模型表示 之前学习过线性回归还是逻辑回归都有这样特点:当特征太,计算负荷会非常大。 这时候我们就要使用神经网络来解决问题了。 那么什么是神经网络...
  • 神经网络入门指南

    2017-12-02 00:11:28
    人工神经网络(ANN)是一种从信息处理角度对人脑神经元网络进行抽象从而建立的某种简单模型,按不同连接方式组成不同网络。其在语音识别、计算机视觉和文本处理等方面取得突破性成果。在下文,我们将深入了解...
  • BP神经网络(BPNN)

    万次阅读 2018-05-28 16:37:47
    本文简要介绍BP神经网络(BPNN, Back Propagation Neural Network)的思想。BP神经网络是最基础的神经网络,结果采用前向传播,误差反向(Back ...作为银行风控部门的负责人,你希望建立个神经网络模型,...
  • 文本预处理 文本是一类序列数据,一篇文章可以看作是字符或单词序列,本节将介绍文本数据常见预处理步骤,预处理通常包括四...将文本从词序列转换为索引序列,方便输入模型 有很现成分词工具包可以使用...
  • 以现场实际数据为输入样本,通过灰狼优化算法(Grey Wolf Optimizer, GWO)得到 Elman 神经网络优化最佳权重和阈值,分别为 18.7482 和 0.014435,之后 建立相应 GWO-Elman 神经网络底板突水预测模型;...
  • 在此基础上提出了一基于主元分析神经网络补偿感应电动机逆解耦控制方法,将补偿后α阶积分逆模型串联到对象的输入建立广义被控对象。复杂感应电动机调速系统被解耦成电磁转矩与定子磁链独立回路,利用...
  • 神经网络一,神经网络模型建立1.1 前向传播 在上一次类别分类器练习中,通过类别逻辑回归算法实现了识别数字笔迹。 但是,逻辑回归算法不能得到一更为复杂假设函数,因为它仅仅只是一线性分类。 一,...
  • 神经网络模型建立在很神经元之上,每一神经元又是一个个学习模型。这些神经元采纳一些特征作为输入,并且根据本身的模型提供一输 出。下图是一以逻辑回归模型作为自身学习模型的神经元示例,在神经网络中...
  • 将近红外光谱(NIRS)与三层径向基神经网络(RBFNN)结合,建立药用真菌云芝中活性成份糖和蛋白快速无损分析模型(NIRS-RBFNN)。采用卷积平滑、傅里叶变换、一阶变换、二阶变换、尺度小波变换和小波包变换对...
  • 神经网络模型建立在很神经元之上,每一神经元又是一个个学习模型。这些神经元(也叫激活单元,activation unit)采纳一些特征作为输出,并且根据本身的模型提供一输出。 我们设计出了类似于神经元的神经网络,...
  • (3)利用相空间重构技术,把消噪后得到状 态矢量作为 分辨小波神经网络的多维输入,构建了多维分辨小 波神经网络 预测模型,将其应用于混沌时间序列预测,并给出了 实现方法。针 对分辨小波神经网络提出了...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 140
精华内容 56
关键字:

多个输入的神经网络模型建立