这节课主要介绍使用keras实现一个数字(mnist)分类实验
SGD中batch_size(min-batch)的意义
pdf 视频
工具
由于TensorFlow和theano(微分库)使用起来比较灵活,上手没那么快,Kears是封装他们的一个库,可以实现常用的网络,而且Kears也已经作为TensorFlow的官方API,这里先使用Kears搭建网络
搭建第一个网络
这个实验是一个识别数字的网络
网络图:
还是按照之前提到的3个步骤:定义函数集(定义网络)、如何找好的函数(定义损失)、找最好的函数(梯度下降)
1定义函数集(定义网络)
def my_model():
model = Sequential()
# hidden layer 1
model.add(Dense(input_dim=28 * 28, units=500))
model.add(Activation('sigmoid'))
# hidden layer 2
model.add(Dense(units=500))
model.add(Activation('sigmoid'))
# output layer
model.add(Dense(units=10))
model.add(Activation('softmax'))
return model
model = my_model()
2如何找好的函数(定义损失)
my_model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
3找最好的函数(梯度下降)
工具不会已经在定义和配置了网络情况下就会梯度下降求最优,我们只要把数据丢进去,包括训练数据(特征x和标签y)
model.fit(x_train, y_train, batch_size=100, epochs=20)
注意还有batch_size
大小说明:
batch_size
就是每次处理的数目,比如数据集有50000笔,batch_size = 100
每次处理随机选100笔example。1个epoch
处理就会处理500个batch,更新500次参数
流程:
- 随机初始化参数
- 处理第一个batch(注意每笔是随机1、31…):
L ′ = l 1 + l 31 + ⋯ L^{\prime}=l^{1}+l^{31}+\cdots L′=l1+l31+⋯
更新参数 - 处理第二个batch(注意每笔是随机2、16…):
L ′ ′ = l 2 + l 16 + ⋯ L^{\prime \prime}=l^{2}+l^{16}+\cdots L′′=l2+l16+⋯
更新参数 - 直到所有batch都被处理过,完成一个epoch
既然每个batch里面的example是随机的,就有可能batch之间有部分重叠,但是这里的随机选取可以防止陷入局部极值点
当batch_size = 1
时就是SGD(Stochastic Gradient Descent),因为每次选了一个example就更新参数。但是实际中在一个batch中可以使用GPU进行并行计算example,比如:
都是计算50000笔数据,batch_size = 10
时要比batch_size = 1
快10倍,事实上,如果GPU支持10个线程,那么并行处理10笔example的速度和单线程处理1笔example的速度是差不多的
当batch_size = 10
处理10个epoch需要170s时,更新参数次数也达到50000次,那么
在速度差不多的情况下,选择更稳定的batch_size = 10
是优于batch_size = 1
的
需要注意的是:batch_size
不能一直增加,达到线程最大数目时,也只能串行计算了。还有一个原因就是SGD的原因,如果batch_size = 10
实际也是一种SGD,但是设置的太大的话,就会往total loss方向走,陷入局部最小(用SGD就是防止进入局部最小)
如果用了GPU而没有设置batch_size
的话,实际上是没有加速的
数据集下载和处理
用的是mnist数据
可以手动下载:具体看http://yann.lecun.com/exdb/mnist/
直接使用keras加载:
from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
也可使用keras提供的函数下载:具体看http://keras.io/datasets/
import tensorflow as tf
tf.keras.datasets.mnist.load_data(path="mnist.npz")
# 保存在 ~/.keras/datasets/mnist.npz
npz是numpy的文件存储,所以使用numpy加载:
path = "../DataSets/mnist.npz"
with np.load(path, allow_pickle=True) as f:
x_train, y_train = f['x_train'], f['y_train']
x_test, y_test = f['x_test'], f['y_test']
# 2维28 * 28转换成1维28 * 28:
x_train = x_train.reshape(x_train.shape[0], 28 * 28)
x_test = x_test.reshape(x_test.shape[0], 28 * 28)
可以看到数据格式是28*28的灰度图,下图显示的是x
对应的y是5,神经网络最后输出的0~9的概率,因此需要把标签y转换成one hot编码,5对应的就是0000010000
,使用np_utils.to_categorical:
y_train = np_utils.to_categorical(y_train, 10)
y_test = np_utils.to_categorical(y_test, 10)
测试还发现把像素值归一化到0~1可以提高准确度:
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train = x_train / 255
x_test = x_test / 255
测试
result_train = model.evaluate(x_train, y_train)
result_test = model.evaluate(x_test, y_test)
print('Train Acc:', result_train[1])
print('Test Acc:', result_test[1])
代码
# from keras import datasets
# import tensorflow as tf
# tf.keras.datasets.mnist.load_data(path="mnist.npz")
import numpy as np
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.layers import Conv2D, MaxPooling2D, Flatten
from keras.optimizers import SGD, Adam
from keras.utils import np_utils
from keras.datasets import mnist
from keras.models import load_model
def load_data():
path = "../DataSets/mnist.npz"
with np.load(path, allow_pickle=True) as f:
x_train, y_train = f['x_train'], f['y_train']
x_test, y_test = f['x_test'], f['y_test']
x_train = x_train.reshape(x_train.shape[0], 28 * 28)
x_test = x_test.reshape(x_test.shape[0], 28 * 28)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train = x_train / 255
x_test = x_test / 255
y_train = np_utils.to_categorical(y_train, 10)
y_test = np_utils.to_categorical(y_test, 10)
return (x_train, y_train), (x_test, y_test)
def model():
model = Sequential()
# hidden layer 1
model.add(Dense(input_dim=28 * 28, units=500))
model.add(Activation('sigmoid'))
# hidden layer 2
model.add(Dense(units=500))
model.add(Activation('sigmoid'))
# output layer
model.add(Dense(units=10))
model.add(Activation('softmax'))
return model
def train(model, path):
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(x_train, y_train, batch_size=100, epochs=20)
model.save(path)
return model
if __name__ == '__main__':
model_path = 'Model/m1.h5'
is_train = True
(x_train, y_train), (x_test, y_test) = load_data()
if os.path.isfile(model_path) and is_train is False:
model = load_model(model_path)
else:
model = model()
train(model, model_path)
result_train = model.evaluate(x_train, y_train)
result_test = model.evaluate(x_test, y_test)
print('Train Acc:', result_train[1])
print('Test Acc:', result_test[1])
结果:
Train Acc: 0.995116651058197
Test Acc: 0.9768000245094299