在这篇小文章中,我们将简要讨论如何使用KERAS这个现在最新的深度学习框架来构造实用的深度学习模型。 深度学习是目前最热门的高级分析技术之一,在很多方面表现出了超越传统机器学习方法的有效性。但是在常用的TensorFlow,CNTK,Theano等计算环境中实现不同的深度学习模型仍然需要耗费很多时间来编写程序。KERAS的出现提供了一个高度抽象的环境来搭建深度学习模型,特别是其简单易用,跟网络结构一一对应的特点使得其迅速在数据科学家这个使用人群中流行起来。 什么是KERASKEARS是Google工程师François Chollet为主创人员,基于Python开发和维护的一个抽象的神经网络建模环境,提供了一系列的API供用户调用构造自己的深度学习网络。KERAS的出发点就是为用户提供一个能够快速实现模型的手段,从而缩短建模迭代的时间,加快模型试验的频率。用KERAS开发者的话说,就是要做好的科研必须尽可能地缩短从想法到实现结果的时间。在业界工作中这也是成功的关键要素之一。 相比较于常见的深度学习环境,比如TensorFlow,CNTK,Theano,Caffe等,KERAS有以下几个不同:
跟这些流行的计算平台一样,KERAS也支持常见的深度学习模型,比如卷积神经网络,循环神经网络以及二者的组合等。 使用KERAS构造深度神经网络有一系列相对固定的步骤:
KERAS和深度学习模型的对应关系KERAS既然是开发出来快速构造深度学习模型的工具,那么它的API和深度学习模型的要素都有很强的对应关系。 正如上面所说,目前的深度学习模型都可以纳入序列模型或者通用模型的,那么我们用图示的方式来表示这个对应关系,方便读者理解。这里网络图为了方便与按行排列的代码对应,对每一层都进行了标注。 下图展示的是一个典型的全连接序列模型: 这个序列模型可以使用如下的KERAS命令快速搭建: Model = Sequential()Model.add(Dense(10, activation=’sigmoid’, input_shape=(8, )) 【隐含层1+输入层】Model.add(Dense(8, activation=’relu’)) 【隐含层2】Model.add(Dense(10, activation=’relu’)) 【隐含层3】Model.add(Dense(5, activation=’softmax’)) 【输出层】 上面的序列模型也可以用通用模型的API描述的结果,其与图中的网络结构有更强的对应关系: x = Input(shape=(8,)) 【输入层】b = Dense(10, activation=’sigmoid’)(x) 【隐含层1】c = Dense(8, activation=’relu’)(b) 【隐含层2】d = Dense(10, activation=’relu’)(c ) 【隐含层3】out = Dense(5, activation=’softmax’)(d) 【输出层】model = Model(inputs=x, outputs=out) 上面也举了另外的比较复杂的例子。在后面的具体案例中,我们也会强调网络结构和对应的KERAS命令,使读者能建立起较强的联系。 使用KERAS构造深度推荐系统推荐系统是机器学习最广泛的应用领域之一,大家熟悉的亚马逊、迪士尼、谷歌、Netflix 等公司都在网页上有其推荐系统的界面,帮助用户更快、更方便地从海量信息中找到有价值的信息。比如亚马逊(www.amazon.com)会给你推荐书、音乐等,迪士尼(video.disney.com)给你推荐最喜欢的卡通人物和迪士尼电影,谷歌搜索更不用说了, Google Play、 Youtube 等也有自己的推荐引擎、推荐视频和应用等。下面是我登陆亚马逊之后的一个推荐页面,可见我之前应该是购买了咖啡机,所以会有相关的产品推荐出来。
推荐系统的最终目的是从百万甚至上亿内容或者商品中把有用的东西高效地显示给用户,这样可以为用户节省很多自行查询的时间,也可以提示用户可能忽略的内容或商品,使用户更有黏性,更愿意花时间待在网站上,从而使商家可以从内容或者商品中赚取更多的利润,即使流量本身也会使商家从广告中受益。 传统上,推荐系统是基于矩阵分解的协同过滤算法,前面也展示了这样的一个简单模型。下面我们着重介绍深度学习推荐系统。这个模型除了能将用户和可选产品联系起来意外,还能将其他辅助数据,比如用户年龄,地区,上网设备以及各种产品属性,联系起来。这里通过嵌入(Embedding)这种技术将不同的信息串在一起作为输入层,再继续搭建不同的神经网络模型,最后一层用预测评分作为输出层。虽然这里的数据只有用户编码和电影产品编码,但是这样的结构可以拓展到包含其他相关数据。下图展示了这样的一个深度模型的结构示意图:
有了这个示意图,我们就可以很方便地用KERAS依次构造。这里我们假设已经将用户和电影产品做了按照One Hot编码形式组织好了。 首先用嵌入层对用户和电影进行嵌入映射: k = 128model1 = Sequential()model1.add(Embedding(n_users + 1, k, input_length = 1))model1.add(Reshape((k,)))model2 = Sequential()model2.add(Embedding(n_movies + 1, k, input_length = 1))model2.add(Reshape((k,))) 这里的k是映射到的空间的维度。在一般的业务系统中我们可能有上百万的用户和产品,经过嵌入映射到128维的实数域上以后显著地降低了整个系统的维度和大小。 以上几句命令实现了上图从最低下到“用户嵌入”和“电影嵌入”这一阶段的编程。 其次,我们需要用第三个神经网络把前面的两个嵌入网络映射所得到的向量叠加在一起: model = Sequential()model.add(Merge([model1, model2], mode = 'concat')) 至此完成了到第一个粗箭头的网络构造。两个网络已经合并为一个网络。 下面的命令依次完成“隐含层128”和“隐含层32”的构造: model.add(Dropout(0.2))model.add(Dense(k, activation = 'relu'))model.add(Dropout(0.5))model.add(Dense(int(k/4), activation = 'relu'))model.add(Dropout(0.5)) 下面继续构造“隐含层8”: model.add(Dense(int(k/16), activation = 'relu'))model.add(Dropout(0.5)) 隐含层构造完毕之后,需要构造输出层。因为是预测连续变量评分,最后一层直接上线性变化: model.add(Dense(1, activation = 'linear')) 至此,模型构造完毕,可以编译了: model.compile(loss = 'mse', optimizer = 'adam') 这里使用了均方差(MSE)作为损失函数,并使用了ADAM优化算法。 下面,为了能训练模型,需要将数据构造为[users, movies]的形式: users = ratings['user_id'].valuesmovies = ratings['movie_id'].valuesX_train = [users, movies] 最后训练模型: model.fit(X_train, y_train, batch_size = 100, epochs = 50) 使用movielens的用户观看电影评分数据进行训练和验证,我们发现这个模型的误差在0.8226左右,大约一个评分等级不到。即使这样一个简单的模型,效果还是比较好的。如果进一步优化结构,或者引入其他信息,误差还可以进一步降低。 使用KERAS构造图像识别系统图像识别是深度学习最典型的应用之一。关于深度学习的图像识别可以追溯很长的历史,其中最具有代表性的例子是手写字体识别和图片识别。手写字体识别主要是用机器正确区别手写体数字 0~9。银行支票上的手写体识别技术就是基于这个技术。图片识别的代表作就是 ImageNet。这个比赛需要团队识别图片中的动物或者物体,把它们正确地分到一千个类别中的其中一个。 图像识别有很多种技术可以实现,目前最主流的技术是深度神经网络,其中尤以卷积神经网络(CNN)最为出名。卷积神经网络(见图1)是一种自动化特征提取的机器学习模型。从数学的角度看,任何一张图片都可以对应到 224 × 224 × 3 或者 32 × 32 × 3 等三维向量,这取决于像素。我们的目标是把这个三维向量(又被称为张量)映射到 N个类别中的一类。神经网络就是建立了这样一个映射关系,或者称为函数。它通过建立网状结构,辅以矩阵的加、乘等运算,最后输出每个图像属于每个类别的概率,并且取概率最高的作为我们的决策依据。 下面是一个典型的序列卷积神经网络模型的结构:
上面这个网络依次展示了卷积网络模型的主要要素:
下面详细介绍一下在KERAS中如何对应地进行编程处理。
在KERAS里,对于图像这种二维数据,一般使用Conv2D这个二维卷积层。Conv2D有几个必备的参数:
对于上面的例子,KERAS里的典型写法是: model.add(Conv2D(filters=16, kernel_size=(3, 3), strides=1, padding=”valid”, input_shape=xtrain.shape[1:]))
对应于图像的最大池化层通过MaxPooling2D,KERAS也支持平均池化层,区别在于取对应局部的平均值作为池化后结果,方法为AveragePooling2D。对应上面的例子,KERAS的命令如下: model.add(MaxPooling2D(pool_size=(3, 3))
把所有步骤组合到一起,我们就可以将图6显示的一个卷积神经网络模型相应地写为KERAS代码了: model=Sequential()model.add(Conv2D(filters=32, kernel_size=(3, 3), padding='same', input_shape=X_train.shape[1:], activation='relu'))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Conv2D(filters=64, kernel_size=(3, 3), padding='valid'))model.add(Activation('relu'))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Flatten())model.add(Dense(128, activation='relu'))model.add(Dense(num_classes, activation='softmax')) 是不是很简单? 要训练这个模型非常简单。我们先编译这个模型并显示其关键信息: model.compile(loss='categorical_crossentropy', optimizer='adagrad', metrics=['accuracy'])model.summary()
我们看到这个模型一共有421642个参数,大多来自于倒数第二层的全连接层。 拟合这个模型也很简单: model.fit(X_train, y_train, epochs=20, verbose=1, batch_size=10, validation_data = (X_test, y_test)) 这里使用最标准的fit方法。其中指定几个核心参数:
下面我们使用这个模型训练识别0-9这十个数字,使用著名的MNIST数据。不过在训练之前还需要提及对数据的处理:
下图的结果显示即使是这个非常简单的模型,其在验证数据上的预测准确率都是非常高的,达到了99.14%。 使用KERAS可以非常方便的构造自己的卷积神经网络,对于比较复杂的情况,也可以使用已经训练好的一些常见的高效模型,比如VGG16,Xception 等做迁移训练来拟合自己的数据。
上图是著名的VGG16模型的结构。根据刚才的学习结果,读者可以很快地模仿这个结构搭建自己的类似模型,但是KERAS在application库里已经提供了现成训练好的VGG16模型供读者读入使用。读者可以引用这个模型,将顶层去掉用自己的数据重新训练,但是底层的参数借用VGG16已经训练好的权重。这就是迁移学习的思路,可以大大降低需要训练的参数数量,加快新模型的开发。这里使用了通用模型以便在现有的VGG16模型上进行修改: model_vgg = VGG16(include_top = False, weights = 'imagenet', input_shape =(224,224,3))model = Flatten(name = 'flatten')(model_vgg.output)model = Dense(10, activation = 'softmax')(model)model_vgg_mnist = Model(model_vgg.input, model, name = 'vgg16') 这里首先引用VGG16模型,但是通过参数include_top=False指定迁移除顶层以外的其余网络结构到自己的模型中。Weights=’imagenet’表示借用的权重是用ImageNet数据训练出来的额。 其次,通过函数方法在修改过的VGG16模型上构造一个新的扁平层用来连接新构造的全连接层,这个全连接层跟前面的模型没有区别。最后把修改过的VGG16模型和新的顶层叠加起来并赋予新的名字vgg16。这样就得到了一个基于VGG16的新模型。 使用KERAS构造时间序列预测模型时间序列是在商业数据或者工程数据中经常出现的一种数据形式,是以时间为次序排列,用来描述和计量一系列过程或者行为的数据的统称。比如每天商店的收入流水或者某个工厂每小时的产品产出都是时间序列数据。一般研究的时间序列数据有两种类型。最常见的是跟踪单一的计量数据随时间变化的情况,即每个时间点上收集的数据是一个一维变量,这种是最常见的,通常的时间序列默认就是这种数据,也是本章研究的对象。另外一种时间序列数据是多个对象或者多个维度的计量数据随时间变化的情况,即每个时间点上收集的数据是一个多维变量,这种一般也被称为纵向数据(Longitudinal Data),但是不属于这里介绍的对象。 在这里我们介绍如何搭建一个LSTM深度学习模型来对在汉口测量的长江每月流量数据进行预测建模。该数据来源于DataMarket 的时间序列数据库,由澳大利亚莫纳什大学的统计学教授Rob Hyndman 创建,收集了数十个公开的时间序列数据集。 汉口长江月流量数据包含从 1865 年 1 月到 1978 年 12 月在汉口记录的长江每月的流量,总计 1368 个数据点。计量单位未知。
在一般的时间序列建模中,都需要检验数据的平稳性,因为传统时间序列建模都是建立在平稳数据的假设之上。这个数据具备非常强的年度周期性。使用传统的统计技术建模的时候都需要侦测周期性,并消除之,对消除周期性之后的数据运用ARIMA模型建模。
我们可以通过周期图谱法(Periodogram)来得到主要的周期幅度。在Python中可以使用scipy.signal.periodogram来得到周期图谱。在这里我们不是使用原始数据,而是使用原始数据的自相关函数的周期图谱来计算主要周期,这样可以抵消噪音的影响。对读入pandas DataFrame的原始数据ts运行下面的程序我们可以得到如下的周期图谱和计算得到的主要周期长度。 import statsmodels.api as smfrom statsmodels.tsa.stattools import acffrom scipy import signalimport peakutils as peakacf_x, acf_ci = acf(ts, alpha=0.05, nlags=36)fs=1f, Pxx_den = signal.periodogram(acf_x, fs)index = peak.indexes(Pxx_den)cycle=(1/f[index[0]]).astype(int)fig = plt.figure()ax0 = fig.add_subplot(111)plt.vlines(f, 0, Pxx_den)plt.plot(f, Pxx_den, marker='o', linestyle='none', color='red')plt.title('Identified Cycle of %i' % (cycle))plt.xlabel('frequency [Hz]')plt.ylabel('PSD [V**2/Hz]')plt.show()print( index, f, Pxx_den)
很明显有一个周期为 12 个月的季节性。虽然考虑到这个数据的本质是长江水文资料, 12 个月的周期是非常自然的预期,但是这个方法展示了对 ACF 序列运用周期图法(periodogram)找季节性周期的可靠性。在传统方法里,这里需要通过取间隔为12 的差分来消除周期性,得到一个尽可能平稳的时间序列,进而采用ARIMA模型建模。在Python里,单周期的时间序列数据,知道周期的长度以后可以直接使用季节性ARIMA模型(SARIMA)来训练。 但是在使用循环神经网络模型的时候我们不用考虑这些情况,可以直接使用长短记忆模型。此外,在使用LSTM这种序列模型的时候在使用LSTM对这种单一时间序列进行建模的时候,一般通过一下步骤:
首先对数据进行标准化,我们使用sklearn包里的MinMaxScaler函数: scaler = MinMaxScaler(feature_range=(0, 1))trainstd = scaler.fit_transform(train.values.astype(float).reshape(-1, 1))teststd = scaler.transform(test.values.astype(float).reshap 其次,我们将训练数据和测试数据组织成需要的格式,这个格式与我们将要建立的LSTM模型有关。这里我们对每个输入构造一个LSTM神经元,一个60个输入单元,每一个对应一个时间步。这60个单元的输出会作为一个全连接层的输入,这个全连接层直接产生下K个连续时间步的输出预测。作为防止过度拟合的正则化手段,我们在LSTM层和全连接层 之间加了一个Dropout层。这个Dropout层在训练的时候会随机放弃一部分权重的更新,但是在进行预测的时候所有权重都会被用到。
对于这样的网络结构,我们需要如下的一个函数来定义我们的数据,即将数据组织成为[批量数,时间步数,滞后特征数]的形式。这个可以通过如下的函数来实现: def create_dataset(dataset, timestep=1, look_back=1, look_ahead=1): from statsmodels.tsa.tsatools import lagmat import numpy as np ds = dataset.reshape(-1, 1) dataX = lagmat(dataset, maxlag=timestep*look_back, trim='both', original='ex') dataY = lagmat(dataset[(timestep*look_back):], maxlag=look_ahead, trim='backward', original='ex') dataX = dataX.reshape(dataX.shape[0], timestep, look_back)[:-(look_ahead-1)] return np.array(dataX), np.array(dataY[:-(look_ahead-1)]) 执行下面的命令就可以生成所需数据: lookback=1lookahead=24timestep=60trainX, trainY = create_dataset(trainstd, timestep=timestep, look_back=lookback, look_ahead=lookahead)trainX, trainY = trainX.astype('float32'), trainY.astype('float32')truthX, truthY = create_dataset(truthstd, timestep=timestep, look_back=lookback, look_ahead=lookahead) 有了如图15的网络图以后,就可以开始定义我们的LSTM深度学习模型。 batch_size=100model = Sequential()model.add(LSTM(48, batch_size=batch_size, \input_shape=(timestep, lookback), kernel_initializer='he_uniform'))model.add(Dropout(0.15))model.add(Dense(lookahead))model.compile(loss='mean_squared_error', optimizer='adam') 调用fit方法就可以快速的训练这个模型。我们指定迭代20次,小批量数为100): model.fit(trainX, trainY, epochs=20, batch_size=batch_size, verbose=1) 下图展示了拟合过程的信息:
那么这个模型的拟合效果如何呢?
我们看到拟合效果还不错。平均绝对误差百分比(MAPE)只有25%不到,比用传统的SARIMA模型效果要好点。其次,LSTM模型一次输出未来24个时间点的预测值,使用起来比用SARIMA迭代预测方便很多。另外需要指出的是我们也可以直接在模型中指定损失函数为MAPE,这样更好优化衡量指标。 小结在这篇短文中,我们介绍了一个目前正在流行起来的深度学习建模环境KERAS。这个建模环境相对于传统的计算环境,比如CNTK,TensorFlow,Theano等具有抽象性高,易用性好的特点,同时又依托于这几种计算环境,具有一定的可拓展性,非常适合于从事深度学习的实践者使用。 我们看到使用KERAS可以非常直观地描述神经网络结构,几乎可以达到所见即所得的情况。我们在文中还分别介绍了三种流行的应用领域,分别是:
|
|
来自: silence_33 > 《人工智能》