赞
踩
- 1.Python作为深度学习最主流的编程语言,在深度学习工程具体实现中提供了Numpy库(Numerical Python extensions)来作为工具,
- 帮助使用者实现深度学习神经网络,甚至用于实现深度学习框架。
- Numpy是一个第三方的Python包,用于科学计算。这个库的前身是1995年就开始开发的一个用于数组运算的库。经过了长时间的发展,
- 基本上成了绝大部分Python科学计算的基础包,当然也包括所有提供Python接口的深度学习框架。
-
- 2.Array模块
- Array,也就是数组,是numpy中最基础的数据结构。最关键的属性是维度和元素类型,在numpy中,可以非常方便地创建各种不同类型的多维数组,
- 并且执行一些基本操作。在深度学习中,如果神经元之间的连接关系涉及到的参数是数组,便可利用Array模块进行设定,来看例子:
- import numpy as np
- def main():
- """
- show np.array related operations
- """
- # a是python中的list类型
- a = [1, 2, 3, 4]
- # 数组化之后b的类型变为 array
- b = np.array(a)
- # a的类型 <type 'list'>
- print "原始的类型为:" + str(type(a))
- # b的类型 <type 'numpy.ndarray'>
- print "数组化之后的类型为:" + str(type(b))
- # shape参数表示array的大小,这里是(4,)
- print "Array的大小为:" + str(b.shape)
- # 调用argmax()函数可以求得array中的最大值的索引,这里是3
- print "Array中最大元素索引为:" + str(b.argmax())
- # 调用max()函数可以求得array中的最大值,这里是4
- print "Array中最大元素值为:" + str(b.max())
- # 调用mean()函数可以求得array中的平均值,这里是2.5
- print "Array中元素平均值为:" + str(b.mean())
-
- if __name__ == '__main__':
- main()
- 3.注意到在导入numpy的时候,代码中将np作为numpy的别名。这是一种习惯性的用法,后面的章节中本书也默认这么使用。
- 例如在机器学习中常用到的矩阵的转置操作,可以通过matrix构建矩阵,transpose函数来实现转置:
- import numpy as np
- def main():
- """
- show matrix's transpose
- """
- # 初始化矩阵x, x为
- # [[ 0 1 2 3]
- # [ 4 5 6 7]
- # [ 8 9 10 11]]
-
- matrix_x = np.array(np.arange(12).reshape((3, 4)))
- print "原始矩阵:\n" + str(matrix_x)
-
- # 利用函数transpose()实现转置,转置后为矩阵t:
- # [[ 0 4 8]
- # [ 1 5 9]
- # [ 2 6 10]
- # [ 3 7 11]]
- matrix_y = matrix_x.transpose()
- print "转置后矩阵:\n" + str(matrix_y)
-
- if __name__ == '__main__':
- main()
-
- 4.对于一维的array,所有Python列表(list)支持的下标操作方法array也都支持,所以在此没有特别列出。
- 既然叫numerical python,numpy本身所具有的基础数学运算也是强大的:
- import numpy as np
- def main():
- """
- run basic operations of numpy
- """
-
- # 绝对值,1
- a_variable = np.abs(-1)
- print "-1的绝对值为:" + str(a_variable)
-
- # sin函数,1.0
- a_variable = np.sin(np.pi / 2)
- print "pi/2的正弦值为:" + str(a_variable)
-
- # tanh逆函数,0.500001071578
- a_variable = np.arctanh(0.462118)
- print "tanh(0.462118)值为:" + str(a_variable)
-
- # e为底的指数函数,20.0855369232
- a_variable = np.exp(3)
- print "e的3次方值为:" + str(a_variable)
-
- # 2的3次方,8
- a_variable = np.power(2, 3)
- print "2的3次方值为:" + str(a_variable)
-
- # 点积,1*3+2*4=11
- a_variable = np.dot([1, 2], [3, 4])
- print "向量[1. 2]与向量[3. 4]点乘值为:" + str(a_variable)
-
- # 开方,5
- a_variable = np.sqrt(25)
- print "25的2次方根值为:" + str(a_variable)
-
- # 求和,10
- a_variable = np.sum([1, 2, 3, 4])
- print "对[1, 2, 3, 4]中元素求和结果为:" + str(a_variable)
-
- # 平均值,5.5
- a_variable = np.mean([4, 5, 6, 7])
- print "对[1, 2, 3, 4]中元素求平均结果为:" + str(a_variable)
-
- # 标准差,0.968245836552
- a_variable = np.std([1, 2, 3, 2, 1, 3, 2, 0])
- print "对[1, 2, 3, 2, 1, 3, 2, 0]中元素求标准差结果为:" + str(a_variable)
-
- if __name__ == '__main__':
- main()
-
- 5.Random模块
- numpy中的随机模块包含了随机数产生和统计分布相关的基本函数。Python本身也有随机模块random,不过numpy的random功能更丰富,
- 随机模块一般会用于深度学习中的一些随机数的生成,seed的生成以及初始值的设定,具体的用法请看下列代码:
- import numpy as np
- def main():
- """
- show random operations in numpy
- """
- # 设置随机数种子
- np.random.seed(42)
-
- # 产生一个1x3,[0,1)之间的浮点型随机数
- # array([[ 0.37454012, 0.95071431, 0.73199394]])
- # 后面的例子就不在注释中给出具体结果了
- print "产生一个1x3,[0,1)之间的浮点型随机数Array:\n" + str(np.random.rand(1, 3))
-
- # 产生一个[0,1)之间的浮点型随机数
- print "产生一个[0,1)之间的浮点型随机数:\n" + str(np.random.random())
-
- # 从a中有放回的随机采样7个
- array_a = np.array([1, 2, 3, 4, 5, 6, 7])
- print "从array_a中有放回的随机采样7个:\n" + str(np.random.choice(array_a, 7))
-
- # 从a中无放回的随机采样7个
- print "从array_a中无放回的随机采样7个:\n" \
- + str(np.random.choice(array_a, 7, replace=False))
-
- # 对a进行乱序并返回一个新的array
- print "对array_a进行乱序并返回一个新的array:\n" + str(np.random.permutation(array_a))
-
- # 生成一个长度为9的随机bytes序列并作为str返回
- # '\x96\x9d\xd1?\xe6\x18\xbb\x9a\xec'
- print "生成一个长度为9的随机bytes序列并作为str返回:\n" + str([np.random.bytes(9)])
-
- if __name__ == '__main__':
- main()
-
- 6.广播机制
- 对于array,默认执行对位运算。涉及到多个array的对位运算需要array的维度一致,如果一个array的维度和另一个array的子维度一致,
- 则在没有对齐的维度上分别执行对位运算,这种机制叫做广播(broadcasting),言语解释比较难,还是看例子理解:
- import numpy as np
- def main():
- """
- show broadcast operation in numpy
- """
- array_a = np.array([
- [1, 2, 3],
- [4, 5, 6]
- ])
-
- array_b = np.array([
- [1, 2, 3],
- [1, 2, 3]
- ])
-
- # 维度一样的array,对位计算
- # array([[2, 4, 6],
- # [5, 7, 9]])
-
- print "相同维度array, 进行对位运算, 结果为:\n" + str(array_a + array_b)
-
- array_c = np.array([
- [1, 2, 3],
- [4, 5, 6],
- [7, 8, 9],
- [10, 11, 12]
- ])
-
- array_d = np.array([2, 2, 2])
- # 广播机制让计算的表达式保持简洁
- # array_d和array_c的每一行分别进行运算
- # array([[ 3, 4, 5],
- # [ 6, 7, 8],
- # [ 9, 10, 11],
- # [12, 13, 14]])
- print "广播机制下, c和d进行每一行分别计算, 结果为:\n" + str(array_c + array_d)
-
- if __name__ == '__main__':
- main()
-
- 7.向量化
- 向量化在深度学习中的应用十分广泛,它是提升计算效率的主要手段之一。对于在机器学习中缩短每次训练的时间是很有意义的,
- 当可用工作时间不变的情况下,更短的单次训练时间可以让程序员有更多的测试机会,进而更早更好的调整神经网络结构和参数。
- 接下来通过一个矩阵相乘的例子来呈现向量化对于代码计算速度的提升效果。
- 首先导入了numpy和time库,它们分别被用于数学计算和统计运行时间。然后准备数据,这里初始化两个1000000维的随机向量v1和v2,v作为计算结果初始化为零。
-
- import time
- import numpy as np
- def main():
- """
- 设置变量tic和toc分别为计算开始时间和结束时间。在非向量化版本中,两个向量相乘的计算过程使用for循环实现。
- 同样使用变量tic和toc记录计算开始和结束时间。向量化版本使用numpy库的numpy.dot()计算矩阵相乘。
- 可以观察到效率提升效果十分显著。非向量化版本的计算时间约为向量化版本计算时间的500倍。可见向量化对于计算速度的提升是很明显的,
- 尤其是在长时间的深度学习训练中,向量化可以帮助开发者节省更多时间。
- """
-
- # 初始化两个1000000维的随机向量v1,v2用于矩阵相乘计算
- vector_1 = np.random.rand(1000000)
- vector_2 = np.random.rand(1000000)
- result = 0
-
- # 矩阵相乘-非向量化版本
- tic = time.time()
- for i in range(1000000):
- result = result + vector_1[i] * vector_2[i]
- toc = time.time()
- print "非向量化-计算结果:" + str(result)
- print "非向量化-计算时间:" + str((toc - tic) * 1000) + "ms" + "\n"
-
- # 矩阵相乘-向量化版本
- tic = time.time()
- result = np.dot(vector_1, vector_2)
- toc = time.time()
- print "向量化-计算结果:" + str(result)
- print "向量化-计算时间:" + str((toc - tic) * 1000)+"ms"
-
- if __name__ == '__main__':
- main()
-
- 图表展示
- Matplotlib非常强大,不过在深度学习中常用的其实只有很基础的一些功能。这里以机器学习中的梯度下降法来展示其图表功能。
- 首先先假设现在需要求解目标函数func(x) = x * x的极小值,由于func是一个凸函数,因此它唯一的极小值同时也是它的最小值,其一阶导函数为dfunc(x) = 2 * x。
-
- #下面这个例子中展示了如何利用梯度下降法寻找x2的极小值,起始检索点为x=-5,学习率为0.5,最终绘制的图像如下图所示,图中红线为检索过程,红点为每次更新的x值所在的点。
- import numpy as np
- import matplotlib.pyplot as plt
- %matplotlib inline
-
- # 目标函数:y=x^2
- def func(x):
- return np.square(x)
-
- # 目标函数一阶导数也即是偏导数:dy/dx=2*x
- def dfunc(x):
- return 2 * x
-
- #接下来实现梯度下降法功能函数GD()
- def GD(x_start, df, epochs, lr):
- """
- 梯度下降法。给定起始点与目标函数的一阶导函数,求在epochs次迭代中x的更新值
- x_start: x的起始点
- df: 目标函数的一阶导函数
- epochs: 迭代周期
- lr: 学习率
- 返回值xs: x在每次迭代后的位置(包括起始点),长度为epochs+1
- """
- xs = np.zeros(epochs+1)
- x = x_start
- xs[0] = x
- for i in range(epochs):
- dx = df(x)
- # v表示x要改变的幅度
- v = - dx * lr
- x += v
- xs[i+1] = x
- return xs
-
- #在Plot()函数中,具体用matplotlib实现了展示梯度下降法搜索最优解的过程
- def Plot():
- # 利用matplotlib绘制图像
- line_x = np.linspace(-5, 5, 100)
- line_y = func(line_x)
-
- x_start = -5
- epochs = 5
-
- lr = 0.3
- #通过目标函数的一阶导函数实现梯度下降法功能
- x = GD(x_start, dfunc, epochs, lr=lr)
-
- color = 'r'
- # plot实现绘制的主功能
- #画出默认的蓝线,画出目标函数y=x^2的在图中的输入输出值曲线
- plt.plot(line_x, line_y, c='b')
- #画出红线为检索过程,横轴是x起始点的值和后续x每次梯度下降之后值,竖轴是x每次梯度下降之后值输入到目标函数中所输出的值
- plt.plot(x, func(x), c=color, label='lr={}'.format(lr))
- #画出红点为每次更新的x值所在的点
- plt.scatter(x, func(x), c=color, )
- # legend函数显示图例
- plt.legend()
- # show函数显示
- plt.show()
-
- #展示了如何利用梯度下降法寻找x2的极小值,起始检索点为x=-5,学习率为0.5,最终绘制的图像如下图所示,图中红线为检索过程,红点为每次更新的x值所在的点。
- Plot()
- Matplotlib也支持图像的存取和显示,并且和OpenCV一类的接口比起来,对于一般的二维矩阵的可视化要方便很多,这一点在机器学习中体现的极为方便。
-
- import matplotlib.pyplot as plt
- #第一个例子是读取一个本地图片并显示
- #读取一张小白狗的照片并显示
- plt.figure('A Little White Dog')
- little_dog_img = plt.imread('little_white_dog.jpg')
- plt.imshow(little_dog_img)
-
- import matplotlib.pyplot as plt
- #第二个例子将读取的原图灰度化,经过灰度像素变换的图直接绘制了两个形状一样,但是值的范围不一样的图案。
- #显示的时候imshow会自动进行归一化,把最亮的值显示为纯白,最暗的值显示为纯黑。
- #这是一种非常方便的设定,尤其是查看深度学习中某个卷积层的响应图时
- #Z是小白狗的照片,img0就是Z,img1是Z做了个简单的变换
- Z = plt.imread('little_white_dog.jpg')
- #在Matlab命令窗口,通过Matlab函数rgb2gray来消除图像的色调和饱和度信息,同时保留亮度,来将RGB图像或彩色图像转换为灰度图像
- Z = rgb2gray(Z)
- img0 = Z
- img1 = 1 - Z
-
- #cmap指定为'gray'用来显示灰度图
- fig = plt.figure('Auto Normalized Visualization')
- ax0 = fig.add_subplot(121)
- ax0.imshow(img0, cmap='gray')
-
- ax1 = fig.add_subplot(122)
- ax1.imshow(img1,cmap='gray')
- plt.show()
- PaddlePaddle实现
- 1.加载包
- 在进行网络配置之前,首先需要加载相应的Python库,并进行初始化操作,如代码清单2-6所示。
- 代码清单2-6 加载包
- """
- Authors: yin xiaoting(y_tink@163.com)
- Date: 2017/11/16
- 使用PaddlePaddle来做线性回归,拟合房屋价格与房屋面积的线性关系,具体步骤如下:
- 1.载入数据和预处理:load_data()
- 2.定义train()和test()用于读取训练数据和测试数据,分别返回一个reader
- 3.初始化
- 4.配置网络结构和设置参数:
- - 定义成本函数cost
- - 创建parameters
- - 定义优化器optimizer
- 5.定义event_handler
- 6.定义trainer
- 7.开始训练
- 8.打印参数和结果print_parameters()
- 9.展示学习曲线plot_costs()
- """
- import matplotlib
- #from paddle.v2.plot import Ploter语句引入了PaddlePaddle的绘图工具包,其内部也是调用了matplotlib包。
- #在默认设置下,运行绘图代码时可能会遇到“no display name and no$DISPLAY environment variable”的问题。
- #这是因为绘图工具函数的默认后端为一些需要图形用户接口(GUI)的后端,而运行的当前环境不满足。
- #为此,可加入import matplotlib和matplotlib.use('Agg')语句重新指定不需要GUI的后端,从而解决该问题。
- matplotlib.use('Agg')
- import matplotlib.pyplot as plt
- import numpy as np
- import paddle.v2 as paddle
-
- 2.数据处理
- 本次数据集使用的是2016年12月份某市某地区的房价分布。为了简化模型,假设影响房价的因素只有房屋面积,因此数据集只有两列。
- 代码清单2-7和2-8展示了数据处理的全部过程,包括数据装载和归一化。
- 代码清单2-7 dataset初始化
- TRAIN_DATA = None #训练样本数
- X_RAW = None #原样本数据
- TEST_DATA = None #测试样本
-
- 代码清单2-8 载入数据
- def load_data(filename, feature_num=2, ratio=0.8):
- """
- 载入数据并进行数据预处理
- Args:
- filename: 数据存储文件,从该文件读取数据
- feature_num: 数据特征数量,此处有2个特征值,即两列数据
- ratio: 训练集占总数据集比例
- Return:
- """
- global TRAIN_DATA, TEST_DATA, X_RAW
- # data = np.loadtxt() 表示将数据载入后以矩阵或向量的形式存储在data中
- # delimiter=',' 表示以','为分隔符
- data = np.loadtxt(filename, delimiter=',')
- #文件中源数据。data.T[0]表示第一列特征值,data.T[1]表示第二列特征值。X_RAW[0]获取第一个元素,X_RAW[1]获取第二个元素。
- X_RAW = data.T[0].copy()
- #1.axis=0 表示按列计算,即计算列值。axis=1 表示按行计算,即计算行值。
- # max(axis=0)取每一列中的最大值,min(axis=0)取每一列中的最小值,sum(axis=0)计算每列的总值。
- # maximums为array([ 199.96, 2000. ]),minimums为array([ 40.09, 202. ]),sum(axis=0)为array([ 82340.75, 529178. ])。
- #2.data.shape 表示行X列,为(870, 2)。data.shape[0]表示data中一共870行,avgs即计算每列的均值为array([ 94.64454023, 608.25057471])
- maximums, minimums, avgs = data.max(axis=0), data.min(axis=0), data.sum(axis=0) / data.shape[0]
-
- #归一化:减掉均值,然后除以原取值范围。
- for i in xrange(feature_num - 1):
- #1.data[:, i] 表示第i列的元素
- #2.第一列特征值进行归一化(data[:, 0] - avgs[0]) / (maximums[0] - minimums[0])
- # 第一列每个列值减去该列平均值,然后除以“该列最大值减该列最小值的”原取值范围
- data[:, i] = (data[:, i] - avgs[i]) / (maximums[i] - minimums[i])
-
- # offset用于划分训练数据集和测试数据集,例如0.8表示训练集占80%
- # offset为第696行,即训练样本数为1到696行,测试样本数为696到870行
- offset = int(data.shape[0] * ratio)
- TRAIN_DATA = data[:offset].copy()
- TEST_DATA = data[offset:].copy()
-
- 3.完成数据集的加载和分割后,PaddlePaddle中通过reader来加载数据。reader返回的数据可以包括多列,利用reader可以使训练组合特征变得更容易。
- 本次的数据只有两维,reader优点不明显,当数据多起来时就会发现利用reader训练模型会比传统方法方便许多。
- 代码清单2-9是利用reader读取训练数据或测试数据,服务于train()和test(),代码清单2-10和2-11是分别获取代码清单2-9中生成的训练数据集和测试数据集。
- 代码清单2-9 读取训练数据或测试数据
- def read_data(data_set):
- """
- 读取训练数据或测试数据,服务于train()和test()
- Args: data_set: 要获取的数据集
- Return: reader: 用于获取训练数据集及其标签的生成器generator
- """
- def reader():
- """
- 一个reader
- Return: data[:-1], data[-1:] -- 使用yield返回生成器(generator),
- data[:-1]表示前n-1个元素,也就是训练数据,data[-1:]表示最后一个元素,也就是对应的标签
- 生成器使用规则:
- 第一种方式:for item in read_data(data_set)(),即for item in 生成器()
- 第二种方式:把返回生成器的函数赋值为一个生成器对象,然后再调用生成器对象()函数执行的方式获取生成器中yield返回的数据
- data_creator = train() #生成器对象 = 生成器()
- for item in data_creator():#for item in 生成器对象()
- item[0],item[1] #获取的便是yield返回的data[:-1],data[-1:]
- """
- #遍历样本集的每一行数据
- for data in data_set:
- #1.带yield返回的是一个生成器generator,用于获取样本集数据和标签数据,生产期返回数据的格式array([训练数据, 标签数据])
- #2.因为左闭右开的原理,所以data[:-1]并不包括最后一个元素,此处即取出每行的第一列特征值即训练数据,
- # data[-1:]因此只能取到最后一个元素,此处即取出每行的第二列特征值即标签数据。
- yield data[:-1], data[-1:]
- return reader
-
- 代码清单2-10 获取训练数据集
- """
- train()返回的是一个生成器,用于在for循环中迭代生成器以每次获取一批数据。
- 生成器使用规则:
- 第一种方式:for item in read_data(data_set)(),即for item in 生成器()
- 第二种方式:把返回生成器的函数赋值为一个生成器对象,然后再调用生成器对象()函数执行的方式获取生成器中yield返回的数据
- data_creator = train() #生成器对象 = 生成器()
- for item in data_creator():#for item in 生成器对象()
- item[0],item[1] #获取的便是yield返回的data[:-1],data[-1:]
- """
- def train():
- """
- 定义一个reader来获取训练数据集及其标签
- Return read_data: 用于获取训练数据集及其标签的reader
- """
- global TRAIN_DATA
- load_data('data.txt')
- return read_data(TRAIN_DATA)#返回的是一个生成器,用于在for循环中迭代生成器以每次获取一批数据
-
- 代码清单2-11 获取测试数据集
- """
- test()返回的是一个生成器,用于在for循环中迭代生成器以每次获取一批数据。
- 生成器使用规则:
- 第一种方式:for item in read_data(data_set)(),即for item in 生成器()
- 第二种方式:把返回生成器的函数赋值为一个生成器对象,然后再调用生成器对象()函数执行的方式获取生成器中yield返回的数据
- data_creator = test() #生成器对象 = 生成器()
- for item in data_creator():#for item in 生成器对象()
- item[0],item[1] #获取的便是yield返回的data[:-1],data[-1:]
- """
- def test():
- """
- 定义一个reader来获取测试数据集及其标签
- Return read_data: 用于获取测试数据集及其标签的reader
- """
- global TEST_DATA
- load_data('data.txt')
- return read_data(TEST_DATA)#返回的是一个生成器,用于在for循环中迭代生成器以每次获取一批数据
- 代码清单2-12 配置网络结构
- def network_config():
- """
- 配置网络结构
- Return:
- cost: 损失函数
- parameters: 模型参数
- optimizer: 优化器
- feeding: 数据映射,python字典
- """
- # 输入层,paddle.layer.data表示数据层,name=’x’:名称为x_input,
- # type=paddle.data_type.dense_vector(1):数据类型为1维稠密向量,定义的标准是一个样本的特征数量(列数)
- x_input = paddle.layer.data(name='x', type=paddle.data_type.dense_vector(1))
-
- # 输出层,paddle.layer.fc表示全连接层,input=x: 该层输入数据为x
- # size=1:神经元个数,act=paddle.activation.Linear():激活函数为Linear()
- #线性回归中使用线性激活函数Linear(),逻辑回归中使用非线性激活函数Sigmoid()
- y_predict = paddle.layer.fc(input=x_input, size=1, act=paddle.activation.Linear())
-
- # 标签数据,paddle.layer.data表示数据层,name=’y’:名称为output_y
- # type=paddle.data_type.dense_vector(1):数据类型为1维稠密向量,定义的标准是一个标签值的维度大小
- y_label = paddle.layer.data(name='y', type=paddle.data_type.dense_vector(1))
-
- # 定义成本函数为均方差损失函数square_error_cost
- cost = paddle.layer.square_error_cost(input=y_predict, label=y_label)
-
- # 利用cost损失函数 创建和初始化 parameters模型参数
- parameters = paddle.parameters.create(cost)
-
- # 创建optimizer优化器,并初始化momentum动量
- # 加入动量项Momentum,可以加速更新过程,比如当接近局部最小时,通过震荡作用,跳出局部最小继续下降到全局最小。
- # 训练过程在更新权重时采用动量优化器 Momentum ,比如momentum=0.9 代表动量优化每次保持前一次速度的0.9倍。
- optimizer = paddle.optimizer.Momentum(momentum=0)
-
- #数据映射的python字典,数据层和数组索引映射,用于trainer训练时喂数据
- feeding = {'x': 0, 'y': 1}
-
- result = [cost, parameters, optimizer, feeding]
- return result
- def main():
- """
- 程序入口,完成初始化,定义神经网络结构,训练,打印等逻辑
- """
- #代码清单2-13 初始化,设置是否使用gpu,trainer数量表示仅使用一个线程进行训练
- paddle.init(use_gpu=False, trainer_count=1)
-
- #代码清单2-14 配置网络结构和设置参数
- #cost 损失函数、parameters 模型参数、optimizer 优化器、feeding 数据映射,python字典
- cost, parameters, optimizer, feeding = network_config()
-
- #代码清单2-15 记录成本cost
- costs = []
-
- #代码清单2-16 构造trainer,SGD定义一个随机梯度下降,配置三个参数cost、parameters、update_equation,它们分别表示成本函数、参数和更新公式。
- trainer = paddle.trainer.SGD(cost=cost, parameters=parameters, update_equation=optimizer)
-
- #在搭建神经网络结构的过程中,仅仅对神经网络的输入进行了描述。而trainer需要读取训练数据进行训练,PaddlePaddle中通过reader来加载数据。
- #reader返回的数据可以包括多列,一个Python dict可以把列序号映射到网络里的数据层。
- #此外,PaddlePaddle还提供事件管理机制event handler,可以用来打印训练的进度及对模型训练效果进行监控,如代码清单2-17所示。
- #代码清单2-17 定义事件处理器,打印训练进度
- # 处理事件
- def event_handler(event):
- """
- 事件处理器,可以根据训练过程的信息作相应操作
- Args: event: 事件对象,包含event.pass_id, event.batch_id, event.cost等信息
- """
- if isinstance(event, paddle.event.EndIteration):
- if event.pass_id % 100 == 0:
- print "Pass %d, Batch %d, Cost %f" % (
- event.pass_id, event.batch_id, event.cost)
- costs.append(event.cost)
-
- if isinstance(event, paddle.event.EndPass):
- #使用测试集数据对训练的模型进行验证输出损失函数值
- result = trainer.test(
- reader=paddle.batch(test(), batch_size=2),
- feeding=feeding)
- print "Test %d, Cost %f" % (event.pass_id, result.cost)
-
-
- #接下来可以调用trainer的train方法启动训练,具体过程如代码清单2-18、2-19、2-20所示。
- #代码清单2-18 模型训练
- # paddle.reader.shuffle(train(), buf_size=500):表示trainer从train()这个reader中读取了buf_size=500大小的数据并打乱顺序
- # paddle.batch(reader(), batch_size=256):表示从打乱的数据中再取出batch_size=256大小的数据进行一次迭代训练
- # feeding:用到了之前定义的feeding索引,将数据层x和y输入trainer
- # event_handler:事件管理机制,可以自定义event_handler,根据事件信息作相应的操作
- # num_passes:定义训练的迭代次数
- # 模型训练
- trainer.train(
- reader=paddle.batch(
- paddle.reader.shuffle(train(), buf_size=500),
- batch_size=256),
- feeding=feeding,
- event_handler=event_handler,
- num_passes=300)
-
- #代码清单2-19 打印参数结果,训练结束后输出回归模型的系数a、b和成本函数变化情况
- print_parameters(parameters)
-
- #代码清单2-20 展示学习曲线
- plot_costs(costs)
-
- #代码清单2-21 参数打印
- def print_parameters(parameters):
- """
- 打印训练结果的参数以及测试结果
- Args: parameters: 训练结果的参数
- """
- print "Result Parameters as below:"
- #获取全连接层的w权重theta_a和b偏置theta_b
- theta_a = parameters.get('___fc_layer_0__.w0')[0]
- theta_b = parameters.get('___fc_layer_0__.wbias')[0]
- print(theta_a, theta_b) #(1137.4955, 608.8786)
-
- #data.T[0]表示第一列特征值,data.T[1]表示第二列特征值。
- #X_RAW[0]获取第一个元素,X_RAW[1]获取第二个元素。
- #TRAIN_DATA[0][0]获取第一个样本数据中的第一个特征值,即第一行的第一列
- #TRAIN_DATA[1][0]获取第二个样本数据中的第一个特征值,即第二行的第一列
- x_0 = X_RAW[0] #x:98.87
- y_0 = theta_a * TRAIN_DATA[0][0] + theta_b #wx+b
-
- x_1 = X_RAW[1] #x:68.74
- y_1 = theta_a * TRAIN_DATA[1][0] + theta_b #wx+b
-
- #wx+b=y:x为房屋面积,y为房屋价格,系数w 即param_a,系数b 即param_b
- #知道预算求购房面积:(y-b)/w,即(y-param_b)/param_a
- param_a = (y_0 - y_1) / (x_0 - x_1)
- param_b = (y_1 - param_a * x_1)
-
- #param_a: 线性回归拟合结果,斜率a
- #param_b: 线性回归拟合结果,截距b
- print 'a = ', param_a
- print 'b = ', param_b
-
- 打印信息:
- Result Parameters as below:
- (1137.4955, 608.8786)
- a = 7.11512781259
- b = -64.529399425
- #模型训练完毕,输出成本函数,如代码清单2-23所示,观察变化,结果如图2-16所示。
- #代码清单2-22 展示模型训练曲线
- def plot_costs(costs):
- """
- 利用costs展示模型的训练曲线
- Args: costs: 记录了训练过程的cost变化的list,每一百次迭代记录一次
- """
- costs = np.squeeze(costs)
- plt.plot(costs)
- plt.ylabel('cost')
- plt.xlabel('iterations (per hundreds)')
- plt.title("House Price Distributions")
- plt.show()
- plt.savefig('costs.png')
- """
- 使用matplotlib.pyplot输出房屋价格与房屋面积的分布以及参数a,b生成的线性回归结果。
- 对比线性回归结果和真实分布,发现该回归结果可以大致根据房屋面积大小预测房屋价格。
- """
- import numpy as np
- import matplotlib.pyplot as plt
-
- def main(param_a, param_b):
- """
- 展示房屋价格与房屋面积的分布以及参数param_a,param_b生成的线性回归结果
- Args:
- param_a: 线性回归拟合结果,斜率a
- param_b: 线性回归拟合结果,截距b
- """
- data = np.loadtxt('data.txt', delimiter=',')
-
- x_area = data[:, 0] #第一列特征值所有值:面积
- y_price = data[:, 1] #第二列特征值所有值:价格
- y_predict = x_area * param_a + param_b #wx+b=y 根据第一列特征值所有面积预测求出对应的房屋价格
- #把样本集数据面积和价格以点的形式来展示
- plt.scatter(x_area, y_price, marker='.', c='r', label='True')
- plt.title('House Price Distributions of XXX City XXX Area')
- plt.xlabel('House Area ')
- plt.ylabel('House Price ')
- plt.xlim(0, 250)
- plt.ylim(0, 2500)
- #根据第一列特征值所有面积和预测求出的对应房屋价格两者信息,画出一条拟合直线
- plt.plot(x_area, y_predict, label='Predict')
- plt.legend(loc='upper left')
- plt.savefig('result.png')
- plt.show()
-
- if __name__ == '__main__':
- main(7.1, -62.3)
- PaddlePaddle实现线性回归-房价预测模型
- 1.基本概念
- 线性回归是机器学习中最简单也是最重要的模型之一,其模型建立同样遵循上图流程:获取数据、数据预处理、训练模型、应用模型。
- 回归模型可以理解为:存在一个点集,用一条曲线去拟合它分布的过程。如果拟合曲线是一条直线,则称为线性回归。
- 如果是一条二次曲线,则被称为二次回归。线性回归是回归模型中最简单的一种。
- 读者可以看到图中成本在刚开始收敛较快,随着迭代次数变多,收敛速度变慢,最终收敛到一个较小值。
-
- 2.在线性回归中有几个基本的概念需要掌握:
- 假设函数(Hypothesis Function):用数学的方法描述自变量和因变量之间的关系,它们之间可以是一个线性函数或非线性函数
- 损失函数(Loss Function):用数学的方法衡量假设函数预测结果与真实值之间的误差。这个差距越小预测越准确,而算法的任务就是使这个差距越来越小。
- 优化算法(Optimization Algorithm):它决定了一个模型的精度和运算速度。本章的线性回归实例中主要使用了梯度下降法进行优化。
-
- 3.损失函数
- 对于某个具体样本(x(i),y(i)),算法通过不断调整参数值和,最终使得预测值和真实值尽可能相似,即整个训练的过程可以表述为通过调整参数值和最小化损失函数。
- 因此,损失函数也是衡量算法优良性的方法。这里涉及到两个值:预测值和真实值。预测值是算法给出的值(用来表示概率)。
- 而真实值是训练集中预先包含的,是事先准备好的。
- 形式上,可以表示为:
- 其中,x(i)表示属于第i个样本的特征向量,y(i)表示属于第i个样本的分类标签,也就是真实值。
- 损失函数的选择需要具体问题具体分析,在不同问题场景下采用不同的函数。通常情况下,会将损失函数定义为平方损失函数(Quadratic Loss Function)。
- 在本次线性回归中,使用的是均方差(Mean Squared Error)来衡量。
- 4.梯度下降算法
- 梯度下降是深度学习中非常重要的概念,值得庆幸的是它也十分容易理解。
- 损失函数J(w,b)可以理解为变量w和b的函数。观察图,垂直轴表示损失函数的值,两个水平轴分别表示变量w和b。
- 实际上,w可能是更高维的向量,但是为了方便说明,在这里假设w和b都是一个实数。算法的最终目标是找到损失函数J(w,b)的最小值。
- 而这个寻找过程就是不断地微调变量w和b的值,一步一步地的试出这个最小值。而试的方法就是沿着梯度方向逐步移动。
- 本例中让图中的圆点表示J(w,b)的某个值,那么梯度下降就是让圆点沿着曲面下降,直到J(w,b)取到最小值或逼近最小值。
- 5.了解了以上几个关键概念后,我们以预测房价为背景,介绍线性回归的PaddlePaddle实现过程。
- 数据集使用了某地区的房价分布,为了简化模型数据只有两维,分别是房屋面积与房屋价格。
- 可以看到房价与房屋面积之间存在一种关系,这种关系究竟是什么,就是本次预测想要得到的结论。
- 6.引用库
- 在进行网络配置之前,首先需要加载相应的Python库,并进行初始化操作。
- numpy:一个python的基本库,用于科学计算
- matplotlib.pyplot:用于生成图,在验证模型准确率和展示成本变化趋势时会使用到
- paddle.v2:paddle深度学习平台
-
- import numpy as np
- import paddle.v2 as paddle
- import matplotlib
- import matplotlib.pyplot as plt
- %matplotlib inline
-
- 7.数据预处理
- 本次数据集使用的是2016年12月份某市某地区的房价分布。为了简化模型,假设影响房价的因素只有房屋面积,因此数据集只有两列。
- 下述代码展示了数据处理的全部过程,包括数据装载和归一化。
- ** 初始化全局变量 **
- # 训练数据、原始数据、测试数据
- CODEMASTER_TRAIN_DATA = None
- X_RAW = None
- CODEMASTER_TEST_DATA = None
-
-
- 8.载入数据并进行预处理
- # 载入数据
- def load_data(filename, feature_num=2, ratio=0.8):
- """
- 载入数据并进行数据预处理
- Args:
- filename -- 数据存储文件,从该文件读取数据
- feature_num -- 数据特征数量
- ratio -- 训练集占总数据集比例
- """
- # 如果测试数据集和训练数据集都不为空,就不再载入数据load_data
- global CODEMASTER_TRAIN_DATA, CODEMASTER_TEST_DATA, X_RAW
- if CODEMASTER_TRAIN_DATA is not None and CODEMASTER_TEST_DATA is not None:
- return
- # data = np.loadtxt()表示将数据载入后以矩阵或向量的形式存储在data中
- # delimiter=',' 表示以','为分隔符
- data = np.loadtxt(filename, delimiter=',')
- X_RAW = data.T[0].copy()
- # axis=0 表示按列计算
- # data.shape[0]表示data中一共多少行
- maximums, minimums, avgs = data.max(axis=0), data.min(axis=0), data.sum(axis=0) / data.shape[0]
-
- # 归一化,data[:, i] 表示第i列的元素
- for i in xrange(feature_num - 1):
- data[:, i] = (data[:, i] - avgs[i]) / (maximums[i] - minimums[i])
-
- # offset用于划分训练数据集和测试数据集,例如0.8表示训练集占80%
- offset = int(data.shape[0] * ratio)
- CODEMASTER_TRAIN_DATA = data[:offset].copy()
- CODEMASTER_TEST_DATA = data[offset:].copy()
-
-
- 9.完成数据集的加载和分割后,PaddlePaddle中通过reader来加载数据。
- Reader返回的数据可以包括多列,利用reader可以使训练组合特征变得更容易。
- 本次的数据只有两维,reader优点不明显,当数据多起来时就会发现利用多个reader训练模型会比传统方法方便许多。
-
- # 读取训练数据或测试数据,服务于train()和test()
- def read_data(data_set):
- """
- 一个reader
- Args:
- data_set -- 要获取的数据集
- Return:
- reader -- 用于获取训练数据集及其标签的生成器generator
- """
- def reader():
- """
- 一个reader
- Return:
- data[:-1], data[-1:] -- 使用yield返回生成器(generator),
- data[:-1]表示前n-1个元素,也就是训练数据,data[-1:]表示最后一个元素,也就是对应的标签
- """
- for data in data_set:
- yield data[:-1], data[-1:]
- return reader
-
- # 获取训练数据集
- def train():
- """
- 定义一个reader来获取训练数据集及其标签
- Return: read_data -- 用于获取训练数据集及其标签的reader
- """
- global CODEMASTER_TRAIN_DATA
- load_data('data.txt')
- return read_data(CODEMASTER_TRAIN_DATA)
-
- # 获取测试数据集
- def test():
- """
- 定义一个reader来获取测试数据集及其标签
- Return: read_data -- 用于获取测试数据集及其标签的reader
- """
- global CODEMASTER_TEST_DATA
- load_data('data.txt')
- return read_data(CODEMASTER_TEST_DATA)
-
- 10.配置网络结构
- 线性回归的模型其实就是一个采用线性激活函数(linear activation,LinearActivation)的全连接层(fully-connected layer,fc_layer),
- 因此在Peddlepeddle中利用全连接层模型构造线性回归,这样一个全连接层就可以看做是一个简单的神经网络。
- 本次的模型由于只有一个影响参数,因此输入只含一个。
- 搭建神经网络就像使用积木搭建宝塔一样。在PaddlePaddle中,网络层(layer)是我们的积木,而神经网络是我们要搭建的宝塔。
- 我们使用不同的layer进行组合,来搭建神经网络。 宝塔的底端需要坚实的基座来支撑,同样,神经网络也需要一些特定的layer作为输入接口,来完成网络的训练。
- 下述代码定义了一个layer组合,其中,x与y为之前描述的输入层;y_predict接收x作为输入,接上一个全连接层;cost接收y_predict与y作为输入,接上均方误差层。
- 最后一层cost中记录了神经网络的所有拓扑结构,通过组合不同的layer,我们即可完成神经网络的搭建。
- 其中x表示输入数据是一个维度为1的稠密向量,y表示输入数据是一个维度为1的稠密向量。
- #配置网络结构
- def netconfig():
- """
- 配置网络结构
- Return:
- x -- 输入层,DATADIM维稠密向量
- y_predict -- 输出层,Linear作为激活函数
- y_label -- 标签数据,1维稠密向量
- cost -- 损失函数
- parameters -- 模型参数
- optimizer -- 优化器
- feeding -- 数据映射,python字典
- """
- # 输入层,paddle.layer.data表示数据层,name=’x’:名称为x,
- # type=paddle.data_type.dense_vector(1):数据类型为1维稠密向量
- x = paddle.layer.data(name='x', type=paddle.data_type.dense_vector(1))
-
- # 输出层,paddle.layer.fc表示全连接层,input=x: 该层输入数据为x
- # size=1:神经元个数,act=paddle.activation.Linear():激活函数为Linear()
- y_predict = paddle.layer.fc(input=x, size=1, act=paddle.activation.Linear())
-
- # 标签数据,paddle.layer.data表示数据层,name=’y’:名称为y
- # type=paddle.data_type.dense_vector(1):数据类型为1维稠密向量
- y = paddle.layer.data(name='y', type=paddle.data_type.dense_vector(1))
-
- # 定义成本函数为均方差损失函数square_error_cost
- cost = paddle.layer.square_error_cost(input=y_predict, label=y)
-
- # 利用cost创建parameters
- parameters = paddle.parameters.create(cost)
-
- # 创建optimizer,并初始化momentum
- # 加入动量项Momentum,可以加速更新过程,比如当接近局部最小时,通过震荡作用,跳出局部最小继续下降到全局最小。
- # 训练过程在更新权重时采用动量优化器 Momentum ,比如momentum=0.9 代表动量优化每次保持前一次速度的0.9倍。
- optimizer = paddle.optimizer.Momentum(momentum=0)
-
- # 数据层和数组索引映射,用于trainer训练时喂数据
- feeding = {'x': 0, 'y': 1}
- data = [x, y_predict, y, cost, parameters, optimizer, feeding]
- return data
-
- 11.训练过程
- 在完成神经网络结构配置之后,首先需要根据神经网络结构来创建所需要优化的参数集合(parameters),并创建优化器(optimizer)。
- 之后,接下来可以创建训练器(trainer)来对网络进行训练。其中,trainer接收三个参数, 配置三个参数cost、parameters、update_equation,
- 它们分别表示成本函数、参数和更新公式。
-
- # 初始化,设置是否使用gpu,trainer数量表示仅使用一个线程进行训练
- paddle.init(use_gpu=False, trainer_count=1)
-
- #配置网络结构
- x, y_predict, y, cost, parameters, optimizer, feeding = netconfig()
-
- # 记录成本cost
- costs = []
-
- # 创建trainer
- trainer = paddle.trainer.SGD(cost=cost, parameters=parameters, update_equation=optimizer)
-
- 12.在配置神经网络结构的过程中,仅仅对神经网络的输入进行了描述。而trainer需要读取训练数据进行训练,PaddlePaddle中通过reader来加载数据。
- Reader返回的数据可以包括多列,一个Python dict可以把列序号映射到网络里的数据层。此外,PaddlePaddle还提供事件管理机制event handler,
- 可以用来打印训练的进度及对模型训练效果进行监控。
-
- # 打印训练过程信息
- def event_handler(event):
- """
- 事件处理器,可以根据训练过程的信息作相应操作
- Args: event -- 事件对象,包含event.pass_id, event.batch_id, event.cost等信息
- """
- if isinstance(event, paddle.event.EndIteration):
- if event.pass_id % 100 == 0:
- print "Pass %d, Batch %d, Cost %f" % (
- event.pass_id, event.batch_id, event.cost)
- costs.append(event.cost)
-
- if isinstance(event, paddle.event.EndPass):
- #使用测试集数据对训练的模型进行验证输出损失函数值
- result = trainer.test(
- reader=paddle.batch(test(), batch_size=2),
- feeding=feeding)
- print "Test %d, Cost %f" % (event.pass_id, result.cost)
-
- 13.模型训练
- 上述内容进行了初始化并配置了网络结构,接下来利用上述配置进行模型训练。
- 首先定义一个随机梯度下降trainer,配置三个参数cost、parameters、update_equation,它们分别表示成本函数、参数和更新公式。
- 再利用trainer.train()即可开始真正的模型训练:
- paddle.reader.shuffle(train(), buf_size=500) 表示trainer从train()这个reader中读取了buf_size=500大小的数据并打乱顺序
- paddle.batch(reader(), batch_size=256) 表示从打乱的数据中再取出batch_size=256大小的数据进行一次迭代训练
- 参数feeding用到了之前定义的feeding索引,将数据层x和label输入trainer,也就是训练数据的来源。
- 参数event_handler是事件管理机制,读者可以自定义event_handler,根据事件信息作相应的操作。
- 参数num_passes=300表示迭代训练300次后停止训练。
-
- # 训练
- trainer.train(
- reader=paddle.batch(
- paddle.reader.shuffle(train(), buf_size=500),
- batch_size=256),
- feeding=feeding,
- event_handler=event_handler,
- num_passes=300)
-
- 14.训练完成后我们可将结果参数打印出来:
- print("Result Parameters as below:")
- a = parameters.get('___fc_layer_0__.w0')[0]
- b = parameters.get('___fc_layer_0__.wbias')[0]
- print(a, b)
-
- x0 = X_RAW[0]
- y0 = a * CODEMASTER_TRAIN_DATA[0][0] + b
-
- x1 = X_RAW[1]
- y1 = a * CODEMASTER_TRAIN_DATA[1][0] + b
-
- a = (y0 - y1) / (x0 - x1)
- b = (y1 - a * x1)
-
- print 'a = ', a
- print 'b = ', b
- #Result Parameters as below:
- #(1137.4955, 608.8786)
- #a = 7.11512781259
- #b = -64.529399425
-
- 15.学习曲线
- 输出成本的变化情况,也就是学习曲线,对模型进行分析,使用matplotlib展示学习曲线:
- 可以看到图中成本在刚开始收敛较快,随着迭代次数变多,收敛速度变慢,最终收敛到一个较小值。
-
- costs = np.squeeze(costs)
- plt.plot(costs)
- plt.ylabel('cost')
- plt.xlabel('iterations (per hundreds)')
- plt.title("House Price Distributions ")
- plt.show()
- #因为训练数据/测试数据的形状都已经转换为(一张图的总像素值即行X列X通道数,样本数),所以此处获取样本数的维度
- m = X.shape[1]
- #1.前向传播,计算成本函数。矩阵之间进行点乘(内积)使用dot()实现向量化操作。
- #2.前向传播中的神经元的第一步操作:通过线性变换(线性函数) 即一次线性回归,执行z=w1x1+w2x2+...+b,最终得到一个实数值
- Z = np.dot(W.T, X) + b
- #3.前向传播中的神经元的第二步操作:通过非线性变换(非线性函数) 即激活函数g(z),常用的激活函数有5种。
- # 操作是把第一步线性变换的结果z输入到激活函数g(z)中,即把实数值转换为概率值。
- # 而在逻辑回归的二分类问题中使用Sigmoid()函数,输出的预测值为概率值(0<=a<=1)。
- A = sigmoid(Z)
- #反向传播:计算参数W的梯度dw和参数b的梯度db,根据链式法则+梯度下降的向量化实现理论得出如下
- # 1.dz=a-y,即预测值减去真实值。
- # 2.dw=(x*dz.T)/m,原始样本数据x和dz的转置之间的矩阵点乘(内积)使用dot()实现向量化操作,然后除以样本数m
- # 3.db=sum(dz)/m,dz的总和除以样本数m
- dZ = A - Y
- #计算多个样本的损失函数(成本函数)。此处cost类型为<class 'list'>
- cost = np.sum(-(Y * np.log(A) + (1 - Y) * np.log(1 - A))) / m
- #后向传播,计算梯度
- #dw:损失函数对参数W的梯度,形状与参数W一致。dw=(x*dz.T)/m,原始样本数据x和dz的转置之间的矩阵点乘(内积)使用dot()实现向量化操作,然后除以样本数m
- dW = np.dot(X, dZ.T) / m
- #db:损失函数对参数b的梯度,形状与参数b一致。db=sum(dz)/m,dz的总和除以样本数m
- db = np.sum(dZ) / m
- #squeeze用于压缩/降维。此处cost类型为<class 'list'>
- cost = np.squeeze(cost)
- #learning_rate: 梯度下降的学习率,可控制收敛速度和效果
- #根据梯度dw更新参数W:W = W - learning_rate * dW
- #根据梯度db更新参数b:b = b - learning_rate * db
- W = W - learning_rate * dW
- b = b - learning_rate * db
- """
- 用于载入数据,目标数据源为两个.h5文件,分别为:
- train_images.h5:训练数据集(猫图片)
- test_images.h5:测试数据集(猫图片)
- """
- import h5py
- import numpy as np
-
- def load_data_sets():
- """
- 用于从两个.h5文件中分别加载训练数据和测试数据
- Return:
- train_x_ori: 原始训练数据集
- train_y: 原始训练数据标签
- test_x_ori: 原始测试数据集
- test_y: 原始测试数据标签
- classes(cat/non-cat): 分类list
- """
- train_data = h5py.File('datasets/train_images.h5', "r")
- #原始训练数据集 train_x_ori.shape为 (209, 64, 64, 3),即(样本数,行,宽,通道数)
- train_x_ori = np.array(train_data["train_set_x"][:])
- #原始训练数据标签 train_y_ori.shape为 (209,),即(样本的标签数,)
- train_y_ori = np.array(train_data["train_set_y"][:])
-
- test_data = h5py.File('datasets/test_images.h5', "r")
- #原始测试数据集 test_x_ori.shape为 (50, 64, 64, 3),即(样本数,行,宽,通道数)
- test_x_ori = np.array(test_data["test_set_x"][:])
- #原始测试数据标签 test_y_ori.shape为 (50,),即(样本的标签数,)
- test_y_ori = np.array(test_data["test_set_y"][:])
- #分类list:array([b'non-cat', b'cat'], dtype='|S7')
- classes = np.array(test_data["list_classes"][:])
-
- #原始训练数据的标签集 train_y_ori.shape为 (1, 209),即(1, 样本的标签数)
- train_y_ori = train_y_ori.reshape((1, train_y_ori.shape[0]))
- #原始测试数据的标签集 test_y_ori.shape为 (1, 50),即(1, 样本的标签数)
- test_y_ori = test_y_ori.reshape((1, test_y_ori.shape[0]))
-
- result = [train_x_ori, train_y_ori, test_x_ori, test_y_ori, classes]
- return result
- """
- 使用python及numpy库来实现逻辑回归识别猫案例,关键步骤如下:
- 1.载入数据和预处理:load_data()
- 2.初始化模型参数(Parameters)
- 3.循环:
- a) 计算成本(Cost)
- b) 计算梯度(Gradient)
- c) 更新参数(Gradient Descent)
- 4.计算准确度
- 5.展示学习曲线plot_costs()
- 6.利用模型进行预测
- """
-
- import matplotlib.pyplot as plt
- import numpy as np
- import utils
-
- def load_data():
- """
- 载入数据,包括训练和测试数据
- Return:
- X_train:原始训练数据集
- Y_train:原始训练数据标签
- X_test:原始测试数据集
- Y_test:原始测试数据标签
- classes(cat/non-cat):分类list
- px_num:数据的像素长度
- """
- X_train, Y_train, X_test, Y_test, classes = utils.load_data_sets()
-
- train_num = X_train.shape[0] #训练样本数为209
- test_num = X_test.shape[0] #测试样本数为50
- px_num = X_train.shape[1] #图片长/宽均为64
-
- #一张图的总像素值即行X列X通道数,即12288
- data_dim = px_num * px_num * 3
- #X_train.shape为(12288, 209) 即训练数据的形状转换为(一张图的总像素值即行X列X通道数,样本数)
- X_train = X_train.reshape(train_num, data_dim).T
- #X_test.shape为(12288, 50) 即测试数据的形状转换为(一张图的总像素值即行X列X通道数,样本数)
- X_test = X_test.reshape(test_num, data_dim).T
- # 归一化
- X_train = X_train / 255.
- X_test = X_test / 255.
- data = [X_train, Y_train, X_test, Y_test, classes, px_num]
- return data
- def sigmoid(x):
- """
- sigmoid 激活函数
- """
- return 1 / (1 + np.exp(-x))
- def initialize_parameters(data_dim):
- """
- 参数W和b初始化为0
- Args: data_dim: W向量的纬度
- Returns:
- W: (dim, 1)维向量
- b: 标量,代表偏置bias
- """
- #data_dim为一张图的总像素值即行X列X通道数,W向量形状为(一张图的总像素值即行X列X通道数,1) 即(12288, 1),代表每个像素点都有一个权重值
- W = np.zeros((data_dim, 1), dtype=np.float)
- b = 0.0 #一个实值
-
- return W, b
- def forward_and_backward_propagate(X, Y, W, b):
- """
- 计算成本Cost和梯度grads
- Args:
- W: 权重, (num_px * num_px * 3, 1)维的numpy数组。W向量形状为(一张图的总像素值即行X列X通道数,1) 即(12288, 1),代表每个像素点都有一个权重值。
- b: 偏置bias。一个实数标量。
- X: 数据,shape为(num_px * num_px * 3, number of examples)。因为训练数据/测试数据的形状都已经转换为(一张图的总像素值即行X列X通道数,样本数)。
- Y: 数据的标签分为cat(y=1)和non-cat(y=0)两类,训练数据的标签集和测试数据的标签集的shape都均为(1, 样本数)。
- Return:
- cost: 逻辑回归的损失函数(成本函数)
- dW: cost对参数W的梯度,形状与参数W一致
- db: cost对参数b的梯度,形状与参数b一致
- """
- #m为样本数,因为训练数据/测试数据的形状都已经转换为(一张图的总像素值即行X列X通道数,样本数),所以此处获取样本数的维度
- m = X.shape[1]
- #1.前向传播,计算成本函数。矩阵之间进行点乘(内积)使用dot()实现向量化操作。
- #2.前向传播中的神经元的第一步操作:通过线性变换(线性函数) 即一次线性回归,执行z=w1x1+w2x2+...+b,最终得到一个实数标量。
- Z = np.dot(W.T, X) + b
- #3.前向传播中的神经元的第二步操作:通过非线性变换(非线性函数) 即激活函数g(z),常用的激活函数有5种。
- # 操作是把第一步线性变换的结果z输入到激活函数g(z)中,即把实数值转换为概率值。
- # 而在逻辑回归的二分类问题中使用Sigmoid()函数,输出的预测值为概率值(0<=a<=1)。
- A = sigmoid(Z)
- #反向传播:计算参数W的梯度dw和参数b的梯度db,根据链式法则+梯度下降的向量化实现理论得出如下
- # 1.dz=a-y,即预测值减去真实值。
- # 2.dw=(x*dz.T)/m,原始样本数据x和dz的转置之间的矩阵点乘(内积)使用dot()实现向量化操作,然后除以样本数m
- # 3.db=sum(dz)/m,dz的总和除以样本数m
- dZ = A - Y
- #计算多个样本的损失函数(成本函数)。此处cost类型为<class 'list'>
- cost = np.sum(-(Y * np.log(A) + (1 - Y) * np.log(1 - A))) / m
- #后向传播(反向传播),计算参数W的梯度dw和参数b的梯度db
- #dw:损失函数对参数W的梯度,形状与参数W一致。dw=(x*dz.T)/m,原始样本数据x和dz的转置之间的矩阵点乘(内积)使用dot()实现向量化操作,然后除以样本数m
- dW = np.dot(X, dZ.T) / m
- #db:损失函数对参数b的梯度,形状与参数b一致。db=sum(dz)/m,dz的总和除以样本数m
- db = np.sum(dZ) / m
- #squeeze用于对矩阵进行压缩/降维。此处cost类型为<class 'list'>
- cost = np.squeeze(cost)
- #构建字典封装数据用于返回
- grads = {
- "dW": dW,
- "db": db
- }
- return grads, cost
- def update_parameters(X, Y, W, b, learning_rate):
- """
- 更新参数
- Args:
- X: 整理后的输入数据
- Y: 标签
- W: 参数W
- b: bias
- learning_rate: 学习步长
- Return:
- W:更新后的参数W
- b:更新后的bias
- cost:成本
- """
-
- #反向传播:计算参数W的梯度dw和参数b的梯度db
- #dW=np.dot(X, dZ.T)/m:损失函数对参数W的梯度,形状与参数W一致。原始样本数据x和dz的转置之间的矩阵点乘(内积)使用dot()实现向量化操作,然后除以样本数m
- #db=np.sum(dZ)/m:损失函数对参数b的梯度,形状与参数b一致。dz的总和除以样本数m
- grads, cost = forward_and_backward_propagate(X, Y, W, b)
- #learning_rate: 梯度下降的学习率,可控制收敛速度和效果
- #根据梯度dw更新参数W:W = W - learning_rate * dW
- #根据梯度db更新参数b:b = b - learning_rate * db
- W = W - learning_rate * grads['dW']
- b = b - learning_rate * grads['db']
- return W, b, cost
- def train(X, Y, W, b, iteration_nums, learning_rate):
- """
- 训练的主过程,使用梯度下降算法优化参数W和b
- Args:
- X: 数据,shape为(num_px * num_px * 3, number of examples)。因为训练数据/测试数据的形状都已经转换为(一张图的总像素值即行X列X通道数,样本数)。
- Y: 数据的标签分为cat(y=1)和non-cat(y=0)两类,训练数据的标签集和测试数据的标签集的shape都均为(1, 样本数)。
- W: 权重, (num_px * num_px * 3, 1)维的numpy数组。W向量形状为(一张图的总像素值即行X列X通道数,1) 即(12288, 1),代表每个像素点都有一个权重值。
- b: 偏置bias,一个实数标量。
- iteration_nums: 训练的迭代次数
- learning_rate: 梯度下降的学习率,可控制收敛速度和效果
- Returns:
- params: 包含参数W和b的python字典
- costs: 保存了优化过程cost的list,可以用于输出cost变化曲线
- """
- costs = []
- for i in range(iteration_nums):
- W, b, cost = update_parameters(X, Y, W, b, learning_rate)
-
- if i % 100 == 0:
- costs.append(cost)
- print "Iteration %d, cost %f" % (i, cost)
-
- params = {
- "W": W,
- "b": b
- }
-
- return params, costs
- def predict_image(X, W, b):
- """
- 用学习到的逻辑回归模型来预测图片是否为猫,数据的标签分为cat(y=1)和non-cat(y=0)两类。
- Args:
- X: 数据,shape为(num_px * num_px * 3, number of examples)。因为训练数据/测试数据的形状都已经转换为(一张图的总像素值即行X列X通道数,样本数)。
- W: 权重, (num_px * num_px * 3, 1)维的numpy数组。W向量形状为(一张图的总像素值即行X列X通道数,1) 即(12288, 1),代表每个像素点都有一个权重值。
- b: 偏置bias,一个实数标量。
- Returns:
- predictions: 包含了对X数据集的所有预测结果,是一个numpy数组或向量
- """
- #data_dim为一张图的总像素值即行X列X通道数,W向量形状为(一张图的总像素值即行X列X通道数,1) 即(12288, 1),代表每个像素点都有一个权重值
- data_dim = X.shape[0]
- #m为样本数,因为训练数据/测试数据的形状都已经转换为(一张图的总像素值即行X列X通道数,样本数),所以此处获取样本数的维度
- m = X.shape[1]
-
- predictions = []
- #W向量形状为(一张图的总像素值即行X列X通道数,1) 即(12288, 1),代表每个像素点都有一个权重值。
- W = W.reshape(data_dim, 1)
- #1.前向传播,计算成本函数。矩阵之间进行点乘(内积)使用dot()实现向量化操作。
- #2.前向传播中的神经元的第一步操作:通过线性变换(线性函数) 即一次线性回归,执行z=w1x1+w2x2+...+b,最终得到一个实数标量。
- #3.前向传播中的神经元的第二步操作:通过非线性变换(非线性函数) 即激活函数g(z),常用的激活函数有5种。
- # 操作是把第一步线性变换的结果z输入到激活函数g(z)中,即把实数值转换为概率值。
- # 而在逻辑回归的二分类问题中使用Sigmoid()函数,输出的预测值为概率值(0<=a<=1)。
- #4.预测概率结果为A 即概率值(0<=a<=1)
- A = sigmoid(np.dot(W.T, X) + b)
-
- # 将连续值A转化为二分类结果0或1
- # 阈值设定为0.5即预测概率大于0.5则预测结果为1
- for i in range(m):
- if A[0, i] >= 0.5:
- predictions.append(1)
- elif A[0, i] < 0.5:
- predictions.append(0)
-
- return predictions
- def calc_accuracy(predictions, Y):
- """
- 计算train准确度
- """
- #squeeze用于对矩阵进行压缩/降维。
- Y = np.squeeze(Y)
- right = 0
- for i in range(len(predictions)):
- if predictions[i] == Y[i]:
- right += 1
- accuracy = (right / float(len(predictions))) * 100
- return accuracy
-
- def plot_costs(costs, learning_rate):
- """
- 利用costs展示模型的学习曲线
- """
- plt.plot(costs)
- plt.ylabel('cost')
- plt.xlabel('Iterations (per hundreds)')
- plt.title("learning rate =" + str(learning_rate))
- # plt.show()
- plt.savefig('costs.png')
- def main():
- #读取数据
- X_train, Y_train, X_test, Y_test, classes, px_num = load_data()
- #迭代次数
- iteration_nums = 2000
- #学习率
- learning_rate = 0.005
- #data_dim为一张图的总像素值即行X列X通道数,W向量形状为(一张图的总像素值即行X列X通道数,1) 即(12288, 1),代表每个像素点都有一个权重值
- data_dim = X_train.shape[0]
- #初始化参数w和b
- W, b = initialize_parameters(data_dim)
- #训练后得出优化好的参数w和b、损失函数
- params, costs = train(X_train, Y_train, W, b, iteration_nums, learning_rate)
- #使用优化好的参数w和b对训练集进行校验
- predictions_train = predict_image(X_train, params['W'], params['b'])
- #使用优化好的参数w和b对测试集进行校验
- predictions_test = predict_image(X_test, params['W'], params['b'])
- #输出训练准确度和测试准确度
- print "Accuracy on train set: {} %".format(calc_accuracy(predictions_train,Y_train))
- print "Accuracy on test set: {} %".format(calc_accuracy(predictions_test,Y_test))
-
- # index(1) is a cat, index(14) not a cat
- index = 15
- #展示指定索引下的图像
- cat_img = X_test[:, index].reshape((px_num, px_num, 3))
- plt.imshow(cat_img)
- plt.axis('off')
- plt.show()
- print "The label of this picture is " + str(Y_test[0, index]) \
- + ", 1 means it's a cat picture, 0 means not " \
- + "\nYou predict that it's a "\
- + classes[int(predictions_test[index])].decode("utf-8") \
- + " picture. \nCongrats!"
- #利用costs展示模型的学习曲线
- plot_costs(costs, learning_rate)
-
- if __name__ == "__main__":
- main()
- """
- 使用paddle框架实现逻辑回归识别猫案例,关键步骤如下:
- 1.载入数据和预处理:load_data()
- 2.定义train()和test()用于读取训练数据和测试数据,分别返回一个reader
- 3.初始化
- 4.配置网络结构和设置参数:
- - 定义成本函数cost
- - 创建parameters
- - 定义优化器optimizer
- 5.定义event_handler
- 6.定义trainer
- 7.开始训练
- 8.预测infer()并输出准确率train_accuracy和test_accuracy
- 9.展示学习曲线plot_costs()
- """
- import matplotlib
- matplotlib.use('Agg')
- import matplotlib.pyplot as plt
- import numpy as np
- import paddle.v2 as paddle
- import utils
- """
- 用于载入数据,目标数据源为两个.h5文件,分别为:
- train_images.h5:训练数据集(猫图片)
- test_images.h5:测试数据集(猫图片)
- """
- import h5py
- import numpy as np
-
- def load_data_sets():
- """
- 用于从两个.h5文件中分别加载训练数据和测试数据
- Return:
- train_x_ori: 原始训练数据集
- train_y: 原始训练数据标签
- test_x_ori: 原始测试数据集
- test_y: 原始测试数据标签
- classes(cat/non-cat): 分类list
- """
- train_data = h5py.File('datasets/train_images.h5', "r")
- #原始训练数据集 train_x_ori.shape为 (209, 64, 64, 3),即(样本数,行,宽,通道数)
- train_x_ori = np.array(train_data["train_set_x"][:])
- #原始训练数据标签 train_y_ori.shape为 (209,),即(样本的标签数,)
- train_y_ori = np.array(train_data["train_set_y"][:])
-
- test_data = h5py.File('datasets/test_images.h5', "r")
- #原始测试数据集 test_x_ori.shape为 (50, 64, 64, 3),即(样本数,行,宽,通道数)
- test_x_ori = np.array(test_data["test_set_x"][:])
- #原始测试数据标签 test_y_ori.shape为 (50,),即(样本的标签数,)
- test_y_ori = np.array(test_data["test_set_y"][:])
- #分类list:array([b'non-cat', b'cat'], dtype='|S7')
- classes = np.array(test_data["list_classes"][:])
-
- #原始训练数据的标签集 train_y_ori.shape为 (1, 209),即(1, 样本的标签数)
- train_y_ori = train_y_ori.reshape((1, train_y_ori.shape[0]))
- #原始测试数据的标签集 test_y_ori.shape为 (1, 50),即(1, 样本的标签数)
- test_y_ori = test_y_ori.reshape((1, test_y_ori.shape[0]))
-
- result = [train_x_ori, train_y_ori, test_x_ori, test_y_ori, classes]
- return result
-
-
- TRAINING_SET = None #包含训练集和标签集
- TEST_SET = None #包含测试集和标签集
- DATA_DIM = None #一张图的总像素值即行X列X通道数,即12288,相当于一个样本的特征数(列数),即一个样本的特征维度
-
- def load_data():
- """
- 载入数据,数据项包括:
- train_set_x_orig:原始训练数据集
- train_set_y:原始训练数据标签
- test_set_x_orig:原始测试数据集
- test_set_y:原始测试数据标签
- classes(cat/non-cat):分类list
- """
- global TRAINING_SET, TEST_SET, DATA_DIM
-
- train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = utils.load_data_sets()
- m_train = train_set_x_orig.shape[0] #训练样本数为209
- m_test = test_set_x_orig.shape[0] #测试样本数为50
- num_px = train_set_x_orig.shape[1] #图片长/宽均为64
-
- # 定义纬度:一张图的总像素值即行X列X通道数,即12288,相当于一个样本的特征数(列数),即一个样本的特征维度
- DATA_DIM = num_px * num_px * 3
-
- # 数据展开,注意此处为了方便处理,没有加上.T的转置操作
- train_set_x_flatten = train_set_x_orig.reshape(m_train, -1) #(209, 12288) 即(样本数,一张图的总像素值即行X列X通道数)
- test_set_x_flatten = test_set_x_orig.reshape(m_test, -1) #(50, 12288) 即(样本数,一张图的总像素值即行X列X通道数)
-
- # 归一化
- train_set_x = train_set_x_flatten / 255.
- test_set_x = test_set_x_flatten / 255.
-
- # 使用numpy.hstack实现numpy数组的横向合并。
- # np.hstack把两个矩阵进行堆叠组合在一起,默认在asix=0的列上进行堆叠,即增加一列,相当于把标签集作为一列增加到训练集中的最后一列的后面。
- # TRAINING_SET 结果形状为(209, 12289)。TEST_SET 结果形状为(50, 12289)。
- TRAINING_SET = np.hstack((train_set_x, train_set_y.T)) # train_set_y.T 结果形状为(209, 1) 即(样本的标签数,1)
- TEST_SET = np.hstack((test_set_x, test_set_y.T)) # test_set_y.T 结果形状(50, 1) 即(样本的标签数,1)
-
- def read_data(data_set):
- """
- 读取训练数据或测试数据,服务于train()和test()
- Args: data_set: 要获取的数据集
- Return: reader: 用于获取训练数据集及其标签的生成器generator
- """
- def reader():
- """
- 一个reader
- Return:
- data[:-1], data[-1:] -- 使用yield返回生成器(generator),
- data[:-1]表示前n-1个元素,也就是训练数据,data[-1:]表示最后一个元素,也就是对应的标签
- 生成器使用规则:
- 第一种方式:for item in read_data(data_set)(),即for item in 生成器()
- 第二种方式:把返回生成器的函数赋值为一个生成器对象,然后再调用生成器对象()函数执行的方式获取生成器中yield返回的数据
- data_creator = train() #生成器对象 = 生成器()
- for item in data_creator():#for item in 生成器对象()
- item[0],item[1] #获取的便是yield返回的data[:-1],data[-1:]
- """
- #遍历样本集的每一行数据
- for data in data_set:
- #1.带yield返回的是一个生成器generator,用于获取样本集数据和标签数据,生产期返回数据的格式array([训练数据, 标签数据])
- #2.因为左闭右开的原理,所以data[:-1]并不包括最后一个元素,此处即取出每行的第一列特征值即训练数据,
- # data[-1:]因此只能取到最后一个元素,此处即取出每行的第二列特征值即标签数据。
- yield data[:-1], data[-1:] #data[:-1]形状为(12288,),data[-1:]形状为(1,)
- return reader
-
- def train():
- """
- 定义一个reader来获取训练数据集及其标签
- Return: read_data: 用于获取训练数据集及其标签的reader
- """
- global TRAINING_SET
- """
- train()返回的是一个生成器,用于在for循环中迭代生成器以每次获取一批数据。
- 生成器使用规则:
- 第一种方式:for item in read_data(data_set)(),即for item in 生成器()
- 第二种方式:把返回生成器的函数赋值为一个生成器对象,然后再调用生成器对象()函数执行的方式获取生成器中yield返回的数据
- data_creator = train() #生成器对象 = 生成器()
- for item in data_creator():#for item in 生成器对象()
- item[0],item[1] #获取的便是yield返回的data[:-1],data[-1:]
- """
- return read_data(TRAINING_SET)
-
- def test():
- """
- 定义一个reader来获取测试数据集及其标签
- Return: read_data: 用于获取测试数据集及其标签的reader
- """
- global TEST_SET
- """
- test()返回的是一个生成器,用于在for循环中迭代生成器以每次获取一批数据。
- 生成器使用规则:
- 第一种方式:for item in read_data(data_set)(),即for item in 生成器()
- 第二种方式:把返回生成器的函数赋值为一个生成器对象,然后再调用生成器对象()函数执行的方式获取生成器中yield返回的数据
- data_creator = test() #生成器对象 = 生成器()
- for item in data_creator():#for item in 生成器对象()
- item[0],item[1] #获取的便是yield返回的data[:-1],data[-1:]
- """
- return read_data(TEST_SET) #返回的是一个生成器,用于在for循环中迭代生成器以每次获取一批数据
- def network_config():
- """
- 配置网络结构和设置参数
- 我们知道Logistic回归模型结构相当于一个只含一个神经元的神经网络,如下图所示,只包含输入数据以及输出层,
- 不存在隐藏层,所以只需配置输入层(input)、输出层(predict)和标签层(label)即可。
- Return:
- y_predict: 输出层,Sigmoid作为激活函数
- cost: 损失函数
- parameters: 模型参数
- optimizer: 优化器
- feeding: 数据映射,python字典
- """
- # 输入层,paddle.layer.data表示数据层,name=’image’:名称为image,
- # type=paddle.data_type.dense_vector(DATA_DIM):数据类型为DATADIM维稠密向量,定义的标准是一个样本的特征数量(列数)
- # 此处DATA_DIM即一个样本(一张图)的总像素值即行X列X通道数,即12288
- image = paddle.layer.data(name='image', type=paddle.data_type.dense_vector(DATA_DIM))
-
- # 输出层,paddle.layer.fc表示全连接层,input=image: 该层输入数据为image
- # size=1:神经元个数,act=paddle.activation.Sigmoid():激活函数为Sigmoid()
- #线性回归中使用线性激活函数Linear(),逻辑回归中使用非线性激活函数Sigmoid()
- y_predict = paddle.layer.fc(input=image, size=1, act=paddle.activation.Sigmoid())
-
- # 标签数据,paddle.layer.data表示数据层,name=’label’:名称为label
- # type=paddle.data_type.dense_vector(1):数据类型为1维稠密向量,定义的标准是一个标签值的维度大小
- y_label = paddle.layer.data(name='label', type=paddle.data_type.dense_vector(1))
-
- # 定义成本函数(损失函数)为 交叉熵损失函数multi_binary_label_cross_entropy_cost
- cost = paddle.layer.multi_binary_label_cross_entropy_cost(input=y_predict,label=y_label)
-
- # 利用cost损失函数 创建和初始化parameters模型参数
- parameters = paddle.parameters.create(cost)
-
- # 创建optimizer,并初始化 momentum动量 和 learning_rate学利率
- # 加入动量项,可以加速更新过程,比如当接近局部最小时,通过震荡作用,跳出局部最小继续下降到全局最小
- # 训练过程在更新权重时采用动量优化器 Momentum ,比如momentum=0.9 代表动量优化每次保持前一次速度的0.9倍。
- optimizer = paddle.optimizer.Momentum(momentum=0, learning_rate=0.00002)
-
- # 数据层和数组索引映射,用于trainer训练时喂数据
- feeding = {
- 'image': 0,
- 'label': 1}
- result = [y_predict, cost, parameters, optimizer, feeding]
- return result
- def get_data(data_creator):
- """
- 使用参数data_creator来获取测试数据
- Args:
- data_creator: 数据来源,可以是train()或者test()
- Return:
- result: 包含测试数据(image)和标签(label)的python字典
- """
- data_creator = data_creator #是一个生成器,用于在for循环中迭代生成器以每次获取一批数据
- #data_image的list中每个元素都是一个元祖,然后元祖里面只有一个值,这个值才是(12288,)的向量 即一个样本数据
- #data_image[i][0] 获取的是list中每个元祖类型的元素中的第一个值,为(12288,)的向量,即array([12288个值])
- data_image = []
- #data_label[i] 获取的是list中每个元素就是一个(1,)的向量,即array([0.])或array([1.])
- data_label = []
-
- """
- train()返回的是一个生成器,用于在for循环中迭代生成器以每次获取一批数据。
- 生成器使用规则:
- 第一种方式:for item in read_data(data_set)(),即for item in 生成器()
- 第二种方式:把返回生成器的函数赋值为一个生成器对象,然后再调用生成器对象()函数执行的方式获取生成器中yield返回的数据
- data_creator = train() #生成器对象 = 生成器()
- for item in data_creator():#for item in 生成器对象()
- item[0],item[1] #获取的便是yield返回的data[:-1],data[-1:]
- """
- for item in data_creator():
- #item[0]形状为(12288,),item[1]形状为(1,)。
- data_image.append((item[0],)) #把(12288,)的向量数据封装到一个元祖中,再把元祖放到list中
- data_label.append(item[1]) #把(1,)的向量直接放到list中
-
- result = {
- "image": data_image,
- "label": data_label
- }
-
- return result
- def calc_accuracy(probs, data):
- """
- 根据数据集来计算准确度accuracy
- Args:
- probs: 数据集的预测结果,调用paddle.infer()来获取
- data: 数据集
- Return:
- calc_accuracy: 训练准确度
- """
- right = 0
- #data['label']的list中每个元素就是一个(1,)的向量,即array([0.])或array([1.])
- total = len(data['label'])
- for i in range(len(probs)):
- if float(probs[i][0]) > 0.5 and data['label'][i] == 1:
- right += 1
- elif float(probs[i][0]) < 0.5 and data['label'][i] == 0:
- right += 1
- accuracy = (float(right) / float(total)) * 100
- return accuracy
- def infer(y_predict, parameters):
- """
- 预测并输出模型准确率
- Args:
- y_predict: 输出层,DATADIM维稠密向量
- parameters: 训练完成的模型参数
- """
- # 获取测试数据和训练数据,用来验证模型准确度
- train_data = get_data(train())
- test_data = get_data(test())
-
- # 根据train_data和test_data预测结果,output_layer表示输出层,parameters表示模型参数,input表示输入的测试数据
- #train_data['image'] 所获取的list中每个元素都是一个元祖,然后元祖里面只有一个值,这个值才是(12288,)的向量 即一个样本数据
- #train_data['image'] 所获取的是list中每个元祖类型的元素中的第一个值,为(12288,)的向量,即array([12288个值])
- probs_train = paddle.infer(
- output_layer=y_predict, parameters=parameters,
- input=train_data['image']
- )
- probs_test = paddle.infer(
- output_layer=y_predict, parameters=parameters,
- input=test_data['image']
- )
-
- # 计算train_accuracy和test_accuracy
- print "train_accuracy: {} %".format(calc_accuracy(probs_train, train_data))
- print "test_accuracy: {} %".format(calc_accuracy(probs_test, test_data))
- def main():
- """
- 定义神经网络结构,训练、预测、检验准确率并打印学习曲线
- """
- global DATA_DIM #一张图的总像素值即行X列X通道数,即12288,相当于一个样本的特征数(列数)
-
- # 载入数据
- load_data()
- # 初始化,设置是否使用gpu,trainer数量表示仅使用一个线程进行训练
- paddle.init(use_gpu=False, trainer_count=1)
- # 配置网络结构和设置参数
- y_predict, cost, parameters, optimizer, feeding = network_config()
- # 记录成本cost
- costs = []
-
- # 处理事件
- def event_handler(event):
- """
- 事件处理器,可以根据训练过程的信息作相应操作
- Args: event -- 事件对象,包含event.pass_id, event.batch_id, event.cost等信息
- """
- if isinstance(event, paddle.event.EndIteration):
- if event.pass_id % 100 == 0:
- print "Pass %d, Batch %d, Cost %f" % (event.pass_id, event.batch_id, event.cost)
- costs.append(event.cost)
- """
- 把parameters参数写出到取parameters.tar文件中,在predict_with_paddle.py中有介绍通过读取parameters.tar文件来获取模型参数进行预测。
- 用学习到的模型进行预测,与train-with-paddle.py不同,在predict_with_paddle.py中不需要重新训练模型,
- 只需要加载训练生成的parameters.tar文件来获取模型参数,对这组参数也就是训练完的模型进行检测。
- 1.载入数据和预处理:load_data()
- 2.从parameters.tar文件直接获取模型参数
- 3.初始化
- 4.配置网络结构
- 5.获取测试数据
- 6.根据测试数据获得预测结果
- 7.将预测结果转化为二分类结果
- 8.预测图片是否为猫
- """
- with open('params_pass_%d.tar' % event.pass_id, 'w') as para_f:
- parameters.to_tar(para_f)
-
- # 构造trainer,SGD定义一个随机梯度下降,配置三个参数cost、parameters、update_equation,它们分别表示成本函数、参数和更新公式。
- trainer = paddle.trainer.SGD(cost=cost, parameters=parameters, update_equation=optimizer)
-
- # 模型训练
- # paddle.reader.shuffle(train(), buf_size=5000):表示trainer从train()这个reader中读取了buf_size=5000大小的数据并打乱顺序
- # paddle.batch(reader(), batch_size=256):表示从打乱的数据中再取出batch_size=256大小的数据进行一次迭代训练
- # feeding:用到了之前定义的feeding索引,将数据层image和label输入trainer
- # event_handler:事件管理机制,可以自定义event_handler,根据事件信息作相应的操作
- # num_passes:定义训练的迭代次数
- trainer.train(
- reader=paddle.batch(
- paddle.reader.shuffle(train(), buf_size=5000),
- batch_size=256),
- feeding=feeding,
- event_handler=event_handler,
- num_passes=5000)
-
- # 预测
- infer(y_predict, parameters)
-
- # 展示学习曲线
- plot_costs(costs)
-
- if __name__ == '__main__':
- main()
-
- def plot_costs(costs):
- """
- 利用costs展示模型的训练曲线
- Args: costs: 记录了训练过程的cost变化的list,每一百次迭代记录一次
- """
- #squeeze可以对矩阵/list进行压缩/降维,costs为一个list
- costs = np.squeeze(costs)
- plt.plot(costs)
- plt.ylabel('cost')
- plt.xlabel('iterations (per hundreds)')
- plt.title("Learning rate = 0.00002")
- # plt.show()
- plt.savefig('costs.png')
- show_image.py
- import matplotlib.pyplot as plt
- import utils
-
- def main():
- """
- show some images in the train dataset
- """
- train_x_ori, train_y, test_x_ori, test_y, classes = \
- utils.load_data_sets()
- print "1. load data from the dataset"
- print "there is " + str(train_y.shape[1]) + " train data and " \
- + str(test_y.shape[1]) + " test data"
- print "train set categories as " + str(classes)
-
- print "2. show train sets label, 0 means not cat, 1 means cat"
- print train_y
-
- print "3. show image(2) with label 1, it should be a cat image"
- index = 2
- plt.imshow(train_x_ori[index])
- plt.pause(30)
-
- print "4. show image(1) with label 0, it should not be a cat image"
- index = 1
- plt.imshow(train_x_ori[index])
- plt.pause(30)
-
- print "5. show test sets label, 0 means not cat, 1 means cat"
- print test_y
-
- print "6. show image(1) with label 1, it should be a cat image"
- index = 1
- plt.imshow(test_x_ori[index])
- plt.pause(30)
-
-
- if __name__ == '__main__':
- main()
-
- predict_with_paddle.py
- """
- 在train-with-paddle.py中通过def event_handler(event)函数把parameters参数写出到parameters.tar文件中,
- 用学习到的模型进行预测,与train-with-paddle.py不同,这里不需要重新训练模型,
- 只需要加载训练生成的parameters.tar文件来获取模型参数,对这组参数也就是训练完的模型进行检测。
- 1.载入数据和预处理:load_data()
- 2.从parameters.tar文件直接获取模型参数
- 3.初始化
- 4.配置网络结构
- 5.获取测试数据
- 6.根据测试数据获得预测结果
- 7.将预测结果转化为二分类结果
- 8.预测图片是否为猫
- """
-
- import numpy as np
- import paddle.v2 as paddle
- from utils import load_data_sets
-
- TEST_SET = None
- PARAMETERS = None
- DATA_DIM = None
- CLASSES = None
-
- def load_data():
- """
- 载入数据,数据项包括:
- train_set_x_orig:原始训练数据集
- train_set_y:原始训练数据标签
- test_set_x_orig:原始测试数据集
- test_set_y:原始测试数据标签
- classes(cat/non-cat):分类list
- """
- global TEST_SET, DATA_DIM, CLASSES
-
- train_x_ori, train_y, test_x_ori, test_y, classes = load_data_sets()
- m_test = test_x_ori.shape[0]
- num_px = train_x_ori.shape[1]
-
- # 定义纬度
- DATA_DIM = num_px * num_px * 3
-
- # 展开数据
- test_x_flatten = test_x_ori.reshape(m_test, -1)
-
- # 归一化数据
- test_x = test_x_flatten / 255.
-
- TEST_SET = np.hstack((test_x, test_y.T))
-
- CLASSES = classes
-
-
- def read_data(data_set):
- """
- 读取训练数据或测试数据,服务于train()和test()
- Args:
- data_set: 要获取的数据集
- Return:
- reader: 用于获取训练数据集及其标签的生成器generator
- """
- def reader():
- """
- 一个reader
- Return:
- data[:-1], data[-1:] -- 使用yield返回生成器(generator),
- data[:-1]表示前n-1个元素,也就是训练数据,data[-1:]表示最后一个元素,也就是对应的标签
- """
- for data in data_set:
- yield data[:-1], data[-1:]
- return reader
-
-
- def test():
- """
- 定义一个reader来获取测试数据集及其标签
- Return:
- read_data: 用于获取测试数据集及其标签的reader
- """
- global TEST_SET
- return read_data(TEST_SET)
-
-
- def get_data(data_creator):
- """
- 获取data,服务于get_train_data()和get_test_data()
- Args:
- data_creator: 数据来源,可以是train()或者test()
- Return:
- result: 包含测试数据(image)和标签(label)的python字典
- """
- data_creator = data_creator
- data_image = []
- data_label = []
-
- for item in data_creator():
- data_image.append((item[0],))
- data_label.append(item[1])
-
- result = {
- "image": data_image,
- "label": data_label
- }
-
- return result
-
-
- def get_binary_result(probs):
- """
- 将预测结果转化为二分类结果
- Args:
- probs: 预测结果
- Return:
- binary_result: 二分类结果
- """
- binary_result = []
- for i in range(len(probs)):
- if float(probs[i][0]) > 0.5:
- binary_result.append(1)
- elif float(probs[i][0]) < 0.5:
- binary_result.append(0)
- return binary_result
-
-
- def network_config():
- """
- 配置网络结构和设置参数
- Return:
- y_predict: 输出层,Sigmoid作为激活函数
- """
- # 输入层,paddle.layer.data表示数据层
- # name=’image’:名称为image
- # type=paddle.data_type.dense_vector(DATA_DIM):数据类型为DATA_DIM维稠密向量
- image = paddle.layer.data(name='image', type=paddle.data_type.dense_vector(DATA_DIM))
-
- # 输出层,paddle.layer.fc表示全连接层,input=image: 该层输入数据为image
- # size=1:神经元个数,act=paddle.activation.Sigmoid():激活函数为Sigmoid()
- y_predict = paddle.layer.fc(input=image, size=1, act=paddle.activation.Sigmoid())
- return y_predict
-
-
- def main():
- """
- main entry 预测结果并检验模型准确率
- """
- global PARAMETERS
-
- # 载入数据
- load_data()
-
- # 从parameters.tar文件中获取模型参数,载入参数
- with open('params_pass_1900.tar', 'r') as param_f:
- PARAMETERS = paddle.parameters.Parameters.from_tar(param_f)
-
- # 初始化
- paddle.init(use_gpu=False, trainer_count=1)
-
- # 配置网络结构
- y_predict = network_config()
-
- # 获取测试数据
- test_data = get_data(test())
-
- # 根据test_data预测结果
- probs = paddle.infer(
- output_layer=y_predict, parameters=PARAMETERS, input=test_data['image']
- )
-
- # 将结果转化为二分类结果
- binary_result = get_binary_result(probs)
-
- # 预测图片是否为猫
- index = 14
- print ("y = " + str(binary_result[index]) +
- ", you predicted that it is a \"" +
- CLASSES[binary_result[index]].decode("utf-8") + "\" picture.")
-
- if __name__ == "__main__":
- main()
- Numpy实现逻辑回归 - 识别猫
- 在该实验中我们将介绍如何使用Python及Numpy lib库实现Logistic回归模型来识别猫。在实现过程中,读者将会学习到神经网络基本结构的配置,
- 其中的关键知识点包括初始化参数、计算成本、计算梯度、优化参数。需要注意的是,在具体的编码实现中会大量是用到Numpy lib库的基本操作,
- 不熟悉numpy操作的读者可以回顾numpy操作内容,方便后续的学习。下面就进入编程实战部分。
-
- 1.图片处理
- 由于识别猫问题涉及到图片处理指示,这里对计算机如何保存图片做一个简单的介绍。在计算机中,图片被存储为三个独立的矩阵,
- 分别对应图3-6中的红、绿、蓝三个颜色通道,如果图片是6464像素的,就会有三个6464大小的矩阵,要把这些像素值放进一个特征向量中,
- 需要定义一个特征向量X,将三个颜色通道中的所有像素值都列出来。如果图片是6464大小的,那么特征向量X的总纬度就是6464*3,也就是12288维。
- 这样一个12288维矩阵就是Logistic回归模型的一个训练数据。
- 2.引用库
- 首先,载入几个需要用到的库,它们分别是:
- numpy:一个python的基本库,用于科学计算
- matplotlib.pyplot:用于生成图,在验证模型准确率和展示成本变化趋势时会使用到
- lr_utils:定义了load_dataset()方法用于载入数据
-
- import numpy as np
- import matplotlib.pyplot as plt
- from lr_utils import load_dataset
-
- 3.载入数据
- 猫的图片数据集以hdf5文件的形式存储,包含了如下内容:
- 训练数据集:包含了m_train个图片的数据集,数据的标签(Label)分为cat(y=1)和non-cat(y=0)两类。
- 测试数据集:包含了m_test个图片的数据集,数据的标签(Label)同(1)。
- 单个图片数据的存储形式为(num_x, num_x, 3),其中num_x表示图片的长或宽(数据集图片的长和宽相同),数字3表示图片的三通道(RGB)。
- 在代码中使用一行代码来读取数据,我们暂不需要了解数据的读取过程,只需调用load_dataset()方法,并存储五个返回值,以便后续的使用。
- 需要注意的是,添加“orig”后缀表示该数据为原始数据,因为之后还需要对数据进行进一步处理。未添加“orig”的数据则表示之后不对该数据作进一步处理。
-
- # 读取数据(cat/non-cat)
- train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()
-
- 上述数据共包含五个部分,分别是训练和测试数据集train_set_x_orig、test_set_x_orig以及对应的标签集train_set_y,test_set_y,
- 还有一个分类列表classes。以训练数据集train_set_x_orig为例,其中每一行都是一个表示图像的三维数组。
- """
- This module provide configure file management service in i18n environment.
- 用于载入数据,目标数据源为两个.h5文件,分别为:
- train_catvnoncat.h5:训练数据集(猫图片)
- test_catvnoncat.h5:测试数据集(猫图片)
- """
- import numpy as np
- import h5py
-
- def load_dataset():
- """
- 用于从两个.h5文件中分别加载训练数据和测试数据
- Return:
- train_set_x_orig -- 原始训练数据集
- train_set_y -- 原始训练数据标签
- test_set_x_orig -- 原始测试数据集
- test_set_y -- 原始测试数据标签
- classes(cat/non-cat) -- 分类list
- """
- train_dataset = h5py.File('datasets/train_catvnoncat.h5', "r")
- train_set_x_orig = np.array(train_dataset["train_set_x"][:]) # your train set features
- train_set_y_orig = np.array(train_dataset["train_set_y"][:]) # your train set labels
-
- test_dataset = h5py.File('datasets/test_catvnoncat.h5', "r")
- test_set_x_orig = np.array(test_dataset["test_set_x"][:]) # your test set features
- test_set_y_orig = np.array(test_dataset["test_set_y"][:]) # your test set labels
-
- classes = np.array(test_dataset["list_classes"][:]) # the list of classes
-
- train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
- test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
-
- dataset = [train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes]
- return dataset
-
- 4.数据预处理
- 1.获取数据后的下一步工作是获得数据的相关信息,如训练数据个数m_train、测试数据个数m_test和图片的长度或宽度num_x,
- 下述代码使用numpy.array.shape来获取数据的相关信息。
-
- # 获取数据相关信息
- m_train = train_set_x_orig.shape[0]
- m_test = test_set_x_orig.shape[0]
- # 本例中num_px=64
- num_px = train_set_x_orig.shape[1]
-
- 2.接下来需要对数据作进一步处理,为了便于训练,可以忽略图片的结构信息,将包含图像长、宽和通道数信息的三维数组压缩成一维数组,
- 图片数据的形状将由(64, 64, 3)转化为(64 * 64 * 3, 1),代码清单3-7给出了转换数据形状的方式。
-
- # 转换数据形状
- train_set_x_flatten = train_set_x_orig.reshape(m_train,-1).T
- test_set_x_flatten = test_set_x_orig.reshape(m_test,-1).T
-
- 3.在开始训练之前,还需要对数据进行归一化处理。图片采用红、绿、蓝三通道的方式来表示颜色,每个通道的单个像素点都存储着一个0-255的像素值,
- 所以图片的归一化处理十分简单,只需要将数据集中的每个值除以255即可,但需要注意的是结果值应为float类型,直接除以255会导致结果错误,
- 在Python中除以255.即可将结果转化为float类型,下述代码给出了数据归一化过程。
-
- train_set_x = train_set_x_flatten/255.
- test_set_x = test_set_x_flatten/255.
-
- 5.模型训练
- 1.完成了数据处理工作,下面开始进入模型训练过程。其中有四个关键步骤分别为:
- 1.初始化模型参数(Parameters)
- 2.循环:
- 计算成本(Cost)
- 计算梯度(Gradient)
- 更新参数(Gradient Descent)
- 3.利用模型进行预测
- 4.分析预测结果
- 2.首先,实现Sigmoid()激活函数如下代码所示,较为简单,不再赘述:
-
- def sigmoid(z):
- s = 1 / (1 + np.exp(-z))
- return s
-
- 3.接下来开始初始化模型参数,定义函数initialize_with_zeros()如代码所示,首先使用numpy.zeros()将w初始化为(dim, 1)形状的零向量,
- 其中dim表示w参数的个数,它的值等于训练数据的特征数,即每张图片的像素点个数。然后再将b初始化为0即可。
-
- def initialize_with_zeros(dim):
- # 将w初始化为(dim, 1)形状的零向量,其中dim表示w参数个数
- # 将b初始化为零
- w = np.zeros((dim, 1), dtype = np.float)
- b = 0
- return w, b
-
- 4.初始化模型参数后,接下来定义前向播和后向传播过程,这两个过程包含在 propagate()函数中。
- 函数propagate()的关键内容是计算成本函数(Cost)和梯度(Gradient),具体的实现如下述代码所示,其中m=X.shape[1]表示数据个数,
- A表示预测结果,cost表示成本函数,dw和db分别表示对应的梯度。
-
- def propagate(w, b, X, Y):
- """
- 计算成本cost和梯度grads
- Args:
- w -- 权重, (num_px * num_px * 3, 1)维的numpy数组
- b -- 偏置bias,标量
- X -- 数据,形状为(num_px * num_px * 3, number of examples)
- Y -- 数据的真实标签(包含值 0 if non-cat, 1 if cat) ,形状为 (1, number of examples)
- Return:
- cost -- 逻辑回归的损失函数
- dw -- cost对参数w的梯度,形状与参数w一致
- db -- cost对参数b的梯度,形状与参数b一致
- """
- # m为数据个数
- m = X.shape[1]
- # 前向传播,计算成本函数
- A = sigmoid(np.dot(w.T, X) + b)
- cost = np.sum(-(Y * np.log(A) + (1 - Y) * np.log(1 - A))) / m
- # 后向传播,计算梯度
- dw = np.dot(X, (A - Y).T) / m
- db = np.sum((A - Y)) / m
- cost = np.squeeze(cost)
- grads = {"dw": dw,"db": db}
- return grads, cost
-
-
- 5.定义了成本函数和梯度的计算过程后,接下来定义优化函数optimize()使用梯度下降更新参数。具体实现如代码所示,
- 关键内容为调用propagate()函数获取梯度值dw、db和cost,并根据梯度值来更新参数w和b。
- 以w为例,具体更新过程为w -= learning_rate * dw。同时,在参数更新过程中,维护一个成本数组costs,
- 每一百次迭代则记录一次成本,便于之后绘图分析成本变化趋势。
-
- # 使用梯度下降更新参数w,b
- def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):
- """
- 使用梯度下降算法优化参数w和b
- Args:
- w -- 权重, (num_px * num_px * 3, 1)维的numpy数组
- b -- 偏置bias,标量
- X -- 数据,形状为(num_px * num_px * 3, number of examples)
- Y -- 数据的真实标签(包含值 0 if non-cat, 1 if cat) ,形状为 (1, number of examples)
- num_iterations -- 优化的迭代次数
- learning_rate -- 梯度下降的学习率,可控制收敛速度和效果
- print_cost -- 每一百次迭代输出一次cost
- Returns:
- params -- 包含参数w和b的python字典
- grads -- 包含梯度dw和db的python字典
- costs -- 保存了优化过程cost的list,可以用于输出cost变化曲线
- """
- costs = []
- for i in range(num_iterations):
- # 调用propagate()获取梯度值grads和成本cost
- grads, cost = propagate(w, b, X, Y)
- dw = grads["dw"]
- db = grads["db"]
-
- # 更新参数
- w -= learning_rate * dw
- b -= learning_rate * db
-
- # 记录costs
- if i % 100 == 0:
- costs.append(cost)
-
- # 每一百次迭代,打印一次cost
- if print_cost and i % 100 == 0:
- print ("Cost after iteration %i: %f" %(i, cost))
-
- params = {"w": w,"b": b}
- grads = {"dw": dw,"db": db}
- return params, grads, costs
-
- 6.模型检验
- 以上内容完成了模型的训练过程,得到了最终的参数w和b,接下来实现predict()函数使用训练完成的模型进行预测,具体实现如代码所示,
- 输入参数w和b以及测试数据集X,预测结果A,并将连续值A转化为二分类结果0或1,存储在Y_prediction中。
-
- # 使用模型进行预测
- def predict(w, b, X):
- """
- 用学习到的逻辑回归模型来预测图片是否为猫(1 cat or 0 non-cat
- Args:
- w -- 权重, (num_px * num_px * 3, 1)维的numpy数组
- b -- 偏置bias,标量
- X -- 数据,形状为(num_px * num_px * 3, number of examples)
- Returns:
- Y_prediction -- 包含了对X数据集的所有预测结果,是一个numpy数组或向量
- """
- # m为数据个数
- m = X.shape[1]
- # 初始化Y_prediction为m维零向量
- Y_prediction = np.zeros((1,m))
- w = w.reshape(X.shape[0], 1)
- # 预测结果A
- A = sigmoid(np.dot(w.T, X) + b)
- for i in range(A.shape[1]):
- # 将连续值A转化为二分类结果0或1
- if A[0, i] > 0.5:
- Y_prediction[0, i] = 1
- else:
- Y_prediction[0, i] = 0
- return Y_prediction
-
- 7.至此,上述内容完成了Logistic回归模型的训练和预测过程,实现了几个关键函数:
- sigmoid():激活函数
- initialize_with_zeros():初始化参数w和b
- propagate():计算成本cost和梯度值dw、db
- optimize():利用梯度下降更新参数值
- predict():使用模型预测结果
-
- 8.实现model
- 现在进行最后一步,实现一个model()函数,将所有函数合并,实现过程十分简单,只需将上述函数按顺序调用即可,具体实现如代码所示,
- 需要注意的是代码中计算了训练准确度train_accuracy和测试准确度test_accuracy,方便读者评估Logistic回归模型。
-
- # 合并所有函数
- def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):
- """
- 按顺序调用上述方法,构建整体逻辑回归模型model
- Args:
- X_train -- 训练数据,形状为(num_px * num_px * 3, m_train)
- Y_train -- 训练数据的真实标签(包含值 0 if non-cat, 1 if cat) ,形状为 (1, m_train)
- X_test -- 测试数据,形状为(num_px * num_px * 3, m_test)
- Y_test -- 测试数据的真实标签(包含值 0 if non-cat, 1 if cat) ,形状为 (1, m_test)
- w -- 权重, (num_px * num_px * 3, 1)维的numpy数组
- b -- 偏置bias,标量
- X -- 数据,形状为(num_px * num_px * 3, number of examples)
- Y -- 数据的真实标签(包含值 0 if non-cat, 1 if cat) ,形状为 (1, number of examples)
- num_iterations -- 优化的迭代次数
- learning_rate -- 梯度下降的学习率,可控制收敛速度和效果
- print_cost -- 每一百次迭代输出一次cost
- Returns:
- d -- 包含模型信息的python字典
- """
- # 初始化参数w,b
- w, b = initialize_with_zeros(X_train.shape[0])
- # 梯度下降更新参数
- parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost)
- # 获得参数w,b
- w = parameters["w"]
- b = parameters["b"]
- # 分别使用训练数据集和测试数据集进行预测
- Y_prediction_test = predict(w, b, X_test)
- Y_prediction_train = predict(w, b, X_train)
- # 输出训练准确度和测试准确度
- print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
- print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))
-
- d = {"costs": costs,
- "Y_prediction_test": Y_prediction_test,
- "Y_prediction_train" : Y_prediction_train,
- "w" : w,
- "b" : b,
- "learning_rate" : learning_rate,
- "num_iterations": num_iterations}
- return d
-
- 利用之前获取并处理过的数据,调用model()函数并开始训练。
- 输出成本cost的变化并输出训练准确率train_accuray和测试准确率test_accuracy。
-
- d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 2000, learning_rate = 0.005, print_cost = True)
-
- Cost after iteration 0: 0.693147
- Cost after iteration 100: 0.584508
- Cost after iteration 200: 0.466949
- Cost after iteration 300: 0.376007
- Cost after iteration 400: 0.331463
- Cost after iteration 500: 0.303273
- Cost after iteration 600: 0.279880
- Cost after iteration 700: 0.260042
- Cost after iteration 800: 0.242941
- Cost after iteration 900: 0.228004
- Cost after iteration 1000: 0.214820
- Cost after iteration 1100: 0.203078
- Cost after iteration 1200: 0.192544
- Cost after iteration 1300: 0.183033
- Cost after iteration 1400: 0.174399
- Cost after iteration 1500: 0.166521
- Cost after iteration 1600: 0.159305
- Cost after iteration 1700: 0.152667
- Cost after iteration 1800: 0.146542
- Cost after iteration 1900: 0.140872
- train accuracy: 99.043062201 %
- test accuracy: 70.0 %
-
- 训练结果显示训练准确率达到99%,说明训练的模型可以准确的拟合训练数据,而测试准确率为70%,
- 由于训练数据集较小并且Logistic回归是一个线性回归分类器,所以70%的准确率已经是一个不错的结果。
-
- 9.预测
- 获得预测结果后,读者可以查看模型对某张图片的预测是否准确,输出图片及其预测的分类结果。
-
- # 分类错误的示例
- index = 12
- plt.imshow(test_set_x[:,index].reshape((num_px, num_px, 3)))
- print ("y = " + str(test_set_y[0,index]) + ", you predicted that it is a \"" +
- classes[int(d["Y_prediction_test"][0,index])].decode("utf-8") + "\" picture.")
- #打印 y = 1, you predicted that it is a "cat" picture.
- 可以看到模型对这张图片的分类正确,将猫图片分类为cat。
- 10.学习曲线
- 现在,根据之前保存的costs输出成本的变化情况
- 可以看到,图中的成本随着迭代次数的增加而减小,这说明了参数w和b不断被学习和优化。
- 至此,Logistic回归模型的Python代码实现已经介绍完毕,相信读者对Logistic回归有了更深刻的理解和把握。
-
- # 输出学习曲线
- costs = np.squeeze(d['costs'])
- plt.plot(costs)
- plt.ylabel('cost')
- plt.xlabel('iterations (per hundreds)')
- plt.title("Learning rate =" + str(d["learning_rate"]))
- plt.show()
- Paddlepaddle实现逻辑回归 - 识别猫
- 欢迎大家来到这个有趣的实验!在这个实验中,大家将学使用PaddlePaddle实现Logistic回归模型来解决识别猫的问题,一步步跟随内容完成训练,
- 加深对逻辑回归理论内容的理解并串联各个知识点,收获对神经网络和深度学习概念的整体把握。
- 你将学会预处理图片数据、利用PaddlePaddle框架实现Logistic回归模型。
- 在开始实验之前,让我们简单介绍一下图片处理的相关知识:由于识别猫问题涉及到图片处理指示,这里对计算机如何保存图片做一个简单的介绍。
- 在计算机中,图片被存储为三个独立的矩阵,分别对应图3-6中的红、绿、蓝三个颜色通道,如果图片是64*64像素的,就会有三个64*64大小的矩阵,
- 要把这些像素值放进一个特征向量中,需要定义一个特征向量X,将三个颜色通道中的所有像素值都列出来。如果图片是64*64大小的,
- 那么特征向量X的总纬度就是64*64*3,也就是12288维。这样一个12288维矩阵就是Logistic回归模型的一个训练数据。
-
- 1.引用库
- 首先,载入几个需要用到的库,它们分别是:
- numpy:一个python的基本库,用于科学计算
- matplotlib.pyplot:用于生成图,在验证模型准确率和展示成本变化趋势时会使用到
- lr_utils:定义了load_datase()方法用于载入数据
- paddle.v2:PaddlePaddle深度学习框架
-
- import sys
- import numpy as np
- import lr_utils
- import matplotlib.pyplot as plt
- import paddle.v2 as paddle
- %matplotlib inline
-
- 2.数据预处理
- 这里简单介绍数据集及其结构。数据集以hdf5文件的形式存储,包含了如下内容:
- 训练数据集:包含了m_train个图片的数据集,数据的标签(Label)分为cat(y=1)和non-cat(y=0)两类。
- 测试数据集:包含了m_test个图片的数据集,数据的标签(Label)同上。
- 单个图片数据的存储形式为(num_x, num_x, 3),其中num_x表示图片的长或宽(数据集图片的长和宽相同),数字3表示图片的三通道(RGB)。
- 在代码中使用一行代码来读取数据,读者暂不需要了解数据的读取过程,只需调用load_dataset()方法,并存储五个返回值,以便后续的使用。
- 需要注意的是,添加“orig”后缀表示该数据为原始数据,因为之后还需要对数据进行进一步处理。未添加“orig”的数据则表示之后不对该数据作进一步处理。
-
- train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = lr_utils.load_dataset()
-
- # 图片示例
- index = 23
- plt.imshow(train_set_x_orig[index])
- print ("y = " + str(train_set_y[:, index]) + ", it's a '" + classes[np.squeeze(train_set_y[:, index])].decode("utf-8") + "' picture.")
- 3.获取数据后的下一步工作是获得数据的相关信息,如训练样本个数m_train、测试样本个数m_test和图片的长度或宽度num_x,使用numpy.array.shape来获取数据的相关信息。
- ** 练习: ** 查看样本信息:
- - m_train (训练样本数)
- - m_test (测试样本数)
- - num_px (图片长或宽)
-
- train_set_x_orig 是一个(m_train, num_px, num_px, 3)形状的numpy数组。
- 举个例子,你可以使用train_set_x_orig.shape[0]来获得 m_train(训练样本数)。
- ### START CODE HERE ### (≈ 3 lines of code)
- m_train = train_set_x_orig.shape[0] #(训练样本数)
- m_test = test_set_x_orig.shape[0] #(测试样本数)
- num_px = test_set_x_orig.shape[1] #(图片长或宽)
- ### END CODE HERE ###
-
- print ("训练样本数: m_train = " + str(m_train))
- print ("测试样本数: m_test = " + str(m_test))
- print ("图片高度/宽度: num_px = " + str(num_px))
- print ("图片大小: (" + str(num_px) + ", " + str(num_px) + ", 3)")
- print ("train_set_x shape: " + str(train_set_x_orig.shape))
- print ("train_set_y shape: " + str(train_set_y.shape))
- print ("test_set_x shape: " + str(test_set_x_orig.shape))
- print ("test_set_y shape: " + str(test_set_y.shape))
-
- #训练样本数: m_train = 209
- #测试样本数: m_test = 50
- #图片高度/宽度: num_px = 64
- #图片大小: (64, 64, 3)
- #train_set_x shape: (209, 64, 64, 3)
- #train_set_y shape: (1, 209)
- #test_set_x shape: (50, 64, 64, 3)
- #test_set_y shape: (1, 50)
- 4.接下来需要对数据作进一步处理,为了便于训练,你可以忽略图片的结构信息,将包含图像长、宽和通道数信息的三维数组压缩成一维数组,
- 图片数据的形状将由(64, 64, 3)转化为(64 * 64 * 3, 1)。
- ** 练习:**
- 将数据形状由(64, 64, 3)转化为(64 * 64 * 3, 1)。
- ** 技巧:**
- 我们可以使用一个小技巧来将(a,b,c,d)形状的矩阵转化为(b ∗ c ∗ d, a)形状的矩阵:X_flatten = X.reshape(X.shape[0], -1)
-
- # 转换数据形状
- ### START CODE HERE ### (≈ 2 lines of code)
- train_set_x_flatten = train_set_x_orig.reshape(m_train,-1) #(训练样本数,-1)
- test_set_x_flatten = test_set_x_orig.reshape(m_test,-1) #(测试样本数,-1)
- ### END CODE HERE ###
-
- print ("train_set_x_flatten shape: " + str(train_set_x_flatten.shape))
- print ("train_set_y shape: " + str(train_set_y.shape))
- print ("test_set_x_flatten shape: " + str(test_set_x_flatten.shape))
- print ("test_set_y shape: " + str(test_set_y.shape))
-
- #train_set_x_flatten shape: (209, 12288)
- #train_set_y shape: (1, 209)
- #test_set_x_flatten shape: (50, 12288)
- #test_set_y shape: (1, 50)
- 5.在开始训练之前,还需要对数据进行归一化处理。图片采用红、绿、蓝三通道的方式来表示颜色,每个通道的单个像素点都存储着一个0-255的像素值,
- 所以图片的归一化处理十分简单,只需要将数据集中的每个值除以255即可,但需要注意的是结果值应为float类型,直接除以255会导致结果错误,
- 在Python中除以255.即可将结果转化为float类型。 现在让我们来归一化数据吧!
- ### START CODE HERE ### (≈ 2 lines of code)
- train_set_x = train_set_x_flatten/255.
- test_set_x = test_set_x_flatten/255.
- ### END CODE HERE ###
-
- #为了方便后续的测试工作,添加了合并数据集和标签集的操作,使用numpy.hstack实现numpy数组的横向合并。
- train_set = np.hstack((train_set_x, train_set_y.T))
- test_set = np.hstack((test_set_x, test_set_y.T))
-
- **经过上面的实验,大家应该记住:**
- 对数据进行预处理的一般步骤是:
- 了解数据的维度和形状等信息,例如(m_train, m_test, num_px, ...)
- 降低数据纬度,例如将数据维度(num_px, num_px, 3)转化为(num_px * num_px * 3, 1)
- 数据归一化
-
- 至此我们就完成了数据预处理工作!在接下来的练习中我们将构造reader,用于读取数据。
-
- 6.构造reader
- 构造read_data()函数,来读取训练数据集train_set或者测试数据集test_set。它的具体实现是在read_data()函数内部构造一个reader(),
- 使用yield关键字来让reader()成为一个Generator(生成器),注意,yield关键字的作用和使用方法类似return关键字,
- 不同之处在于yield关键字可以构造生成器(Generator)。虽然我们可以直接创建一个包含所有数据的列表,但是由于内存限制,
- 我们不可能创建一个无限大的或者巨大的列表,并且很多时候在创建了一个百万数量级别的列表之后,我们却只需要用到开头的几个或几十个数据,
- 这样造成了极大的浪费,而生成器的工作方式是在每次循环时计算下一个值,不断推算出后续的元素,不会创建完整的数据集列表,从而节约了内存使用。
- ** 练习:**现在让我们使用yield来构造一个reader()吧!
-
- # 读取训练数据或测试数据
- def read_data(data_set):
- """
- 一个reader
- Args: data_set -- 要获取的数据集
- Return: reader -- 用于获取训练数据集及其标签的生成器generator
- """
- def reader():
- """
- 一个reader
- Return:
- data[:-1], data[-1:] -- 使用yield返回生成器(generator),
- data[:-1]表示前n-1个元素,也就是训练数据,data[-1:]表示最后一个元素,也就是对应的标签
- """
- for data in data_set:
- ### START CODE HERE ### (≈ 2 lines of code)
- yield data[:-1], data[-1:]
- ### END CODE HERE ###
- return reader
-
- test_array = [[1,1,1,1,0],
- [2,2,2,2,1],
- [3,3,3,3,0]]
-
- print("test_array for read_data:")
- for value in read_data(test_array)():
- print(value)
-
- #test_array for read_data:
- #([1, 1, 1, 1], [0])
- #([2, 2, 2, 2], [1])
- #([3, 3, 3, 3], [0])
- 7.训练过程
- 1.完成了数据的预处理工作并构造了read_data()来读取数据,接下来将进入模型的训练过程,使用PaddlePaddle来定义构造可训练的Logistic回归模型,
- 关键步骤如下:
- 1.初始化
- 2.配置网络结构和设置参数
- 1.配置网络结构
- 2.定义损失函数cost
- 3.创建parameters
- 4.定义优化器optimizer
- 3.模型训练
- 4.模型检验
- 5.预测
- 6.绘制学习曲线
-
- 2.初始化
- 首先进行最基本的初始化操作,在PaddlePaddle中使用paddle.init(use_gpu=False, trainer_count=1)来进行初始化:
- use_gpu=False 表示不使用gpu进行训练
- trainer_count=1 表示仅使用一个训练器进行训练,trainer数量表示仅使用一个线程进行训练
-
- # 初始化
- paddle.init(use_gpu=False, trainer_count=1)
-
- 3.配置网络结构和设置参数
- 1.配置网络结构
- 我们知道Logistic回归模型结构相当于一个只含一个神经元的神经网络,如下图所示,只包含输入数据以及输出层,
- 不存在隐藏层,所以只需配置输入层(input)、输出层(predict)和标签层(label)即可。
- 2.练习:
- 接下来让我们使用PaddlePaddle提供的接口开始配置Logistic回归模型的简单网络结构吧,一共需要配置三层:
- 1.输入层:
- 我们可以定义image=paddle.layer.data(name=”image”,type=paddle.data_type.dense_vector(data_dim))来表示生成一个数据输入层,
- 名称为“image”,数据类型为data_dim维向量;
- 在定义输入层之前,我们需要使用之前计算的num_px来获取数据维度data_dim,data_dim=num_px * num_px * 3
-
- 2.输出层:
- 我们可以定义predict=paddle.layer.fc(input=image, size=1, act=paddle.activation.Sigmoid())表示生成一个全连接层,
- 输入数据为image,神经元个数为1,激活函数为Sigmoid();
-
- 3.标签层:
- 我们可以定义label=paddle.layer.data(name=”label”, type=paddle.data_type.dense_vector(1))表示生成一个数据层,
- 名称为“label”,数据类型为1维向量。
-
- 3.配置网络结构
- # 配置网络结构
- # 数据层需要使用到数据维度data_dim,根据num_px来计算data_dim
- data_dim = num_px * num_px * 3
-
- # 输入层,paddle.layer.data表示数据层
- image = paddle.layer.data(name='image', type=paddle.data_type.dense_vector(data_dim))
-
- # 输出层,paddle.layer.fc表示全连接层
- predict = paddle.layer.fc(input=image, size=1, act=paddle.activation.Sigmoid())
-
- # 标签数据层,paddle.layer.data表示数据层
- label = paddle.layer.data(name='label', type=paddle.data_type.dense_vector(1))
-
- 4.定义损失函数
- 在配置网络结构之后,我们需要定义一个损失函数来计算梯度并优化参数,在这里我们可以使用PaddlePaddle提供的交叉熵损失函数,
- 定义cost = paddle.layer.multi_binary_label_cross_entropy_cost(input=predict, label=label),使用predict与label计算成本。
-
- # 损失函数,使用交叉熵损失函数
- cost = paddle.layer.multi_binary_label_cross_entropy_cost(input=predict, label=label)
-
- 5.创建parameters
- PaddlePaddle中提供了接口paddle.parameters.create(cost)来创建和初始化参数,参数cost表示基于我们刚刚创建的cost损失函数来创建和初始化参数。
-
- # 创建parameters
- parameters = paddle.parameters.create(cost)
-
- 6.创建optimizer
- 参数创建完成后,定义参数优化器optimizer= paddle.optimizer.Momentum(momentum=0, learning_rate=0.00002),使用Momentum作为优化器,
- 并设置动量momentum为零,学习率为0.00002。注意,读者暂时无需了解Momentum的含义,只需要学会使用即可。
-
- #创建optimizer
- # 加入动量项Momentum,可以加速更新过程,比如当接近局部最小时,通过震荡作用,跳出局部最小继续下降到全局最小。
- # 训练过程在更新权重时采用动量优化器 Momentum ,比如momentum=0.9 代表动量优化每次保持前一次速度的0.9倍。
- optimizer = paddle.optimizer.Momentum(momentum=0, learning_rate=0.00002)
-
- 7.其它配置
- feeding={‘image’:0, ‘label’:1}是数据层名称和数组索引的映射,用于在训练时输入数据,costs数组用于存储cost值,记录成本变化情况。
- 最后定义函数event_handler(event)用于事件处理,事件event中包含batch_id,pass_id,cost等信息,我们可以打印这些信息或作其它操作。
-
- # 数据层和数组索引映射,用于trainer训练时喂数据
- feeding = {'image': 0, 'label': 1}
- # 记录成本cost
- costs = []
- # 事件处理
- def event_handler(event):
- """
- 事件处理器,可以根据训练过程的信息作相应操作
- Args: event -- 事件对象,包含event.pass_id, event.batch_id, event.cost等信息
- """
- if isinstance(event, paddle.event.EndIteration):
- if event.pass_id % 100 == 0:
- print("Pass %d, Batch %d, Cost %f" % (event.pass_id, event.batch_id, event.cost))
- costs.append(event.cost)
-
- 4.模型训练
- 1.上述内容进行了模型初始化、网络结构的配置并创建了损失函数、参数、优化器,接下来利用上述配置进行模型训练。
- 首先定义一个随机梯度下降trainer,配置三个参数cost、parameters、update_equation,它们分别表示损失函数、参数和更新公式。
- 再利用trainer.train() 即可开始真正的模型训练,我们可以设置参数如下:
- paddle.reader.shuffle(train(), buf_size=5000) 表示trainer从train()这个reader中读取了buf_size=5000大小的数据并打乱顺序
- paddle.batch(reader(), batch_size=256) 表示从打乱的数据中再取出batch_size=256大小的数据进行一次迭代训练
- 参数feeding 用到了之前定义的feeding索引,将数据层image和label输入trainer,也就是训练数据的来源。
- 参数event_handler 是事件管理机制,读者可以自定义event_handler,根据事件信息作相应的操作。
- 参数num_passes=5000 表示迭代训练5000次后停止训练。
-
- 2.练习:
- 定义trainer并开始训练模型(大家可以自己定义buf_size,batch_size,num_passes等参数,但是建议大家先参考上述的参数设置,
- 在完成整个训练之后,再回过头来调整这些参数,看看结果会有什么不同)
-
- # 构造trainer,定义一个随机梯度下降trainer,配置三个参数cost、parameters、update_equation,它们分别表示损失函数、参数和更新公式
- trainer = paddle.trainer.SGD(cost=cost, parameters=parameters, update_equation=optimizer)
-
- # 模型训练
- trainer.train(
- reader=paddle.batch(
- paddle.reader.shuffle(read_data(train_set), buf_size=5000),
- batch_size=256),
- feeding=feeding,
- event_handler=event_handler,
- num_passes=2000)
-
- Pass 0, Batch 0, Cost 0.718985
- Pass 100, Batch 0, Cost 0.497979
- Pass 200, Batch 0, Cost 0.431610
- Pass 300, Batch 0, Cost 0.386631
- Pass 400, Batch 0, Cost 0.352110
- Pass 500, Batch 0, Cost 0.324237
- Pass 600, Batch 0, Cost 0.301005
- Pass 700, Batch 0, Cost 0.281201
- Pass 800, Batch 0, Cost 0.264035
- Pass 900, Batch 0, Cost 0.248960
- Pass 1000, Batch 0, Cost 0.235580
- Pass 1100, Batch 0, Cost 0.223603
- Pass 1200, Batch 0, Cost 0.212803
- Pass 1300, Batch 0, Cost 0.203003
- Pass 1400, Batch 0, Cost 0.194064
- Pass 1500, Batch 0, Cost 0.185871
- Pass 1600, Batch 0, Cost 0.178331
- Pass 1700, Batch 0, Cost 0.171367
- Pass 1800, Batch 0, Cost 0.164913
- Pass 1900, Batch 0, Cost 0.158914
-
- 3.模型检验
- 模型训练完成后,接下来检验模型的准确率。
- 在这里我们首先需要定义一个函数get_data()来帮助我们获得用于检验模型准确率的数据,而数据的来源是read_data()返回的训练数据和测试数据。
-
- # 获取数据
- def get_data(data_creator):
- """
- 使用参数data_creator来获取测试数据
- Args: data_creator -- 数据来源,可以是train()或者test()
- Return: result -- 包含测试数据(image)和标签(label)的python字典
- """
- data_creator = data_creator
- data_image = []
- data_label = []
-
- for item in data_creator():
- data_image.append((item[0],))
- data_label.append(item[1])
-
- result = {
- "image": data_image,
- "label": data_label
- }
- return result
-
- 4.获得数据之后,我们就可以开始利用paddle.infer()来进行预测,参数output_layer 表示输出层,参数parameters表示模型参数,参数input表示输入的测试数据。
- 练习:
- 利用get_data()获取测试数据和训练数据
- 使用paddle.infer()进行预测
-
- # 获取测试数据和训练数据,用来验证模型准确度
- train_data = get_data(read_data(train_set))
- test_data = get_data(read_data(test_set))
-
- # 根据train_data和test_data预测结果,output_layer表示输出层,parameters表示模型参数,input表示输入的测试数据
- probs_train = paddle.infer(output_layer=predict, parameters=parameters, input=train_data['image'])
- probs_test = paddle.infer(output_layer=predict, parameters=parameters, input=test_data['image'])
-
- 5.获得检测结果probs_train和probs_test之后,我们将结果转化为二分类结果并计算预测正确的结果数量,
- 定义train_accuracy和test_accuracy来分别计算训练准确度和测试准确度。注意,在test_accuracy中,我们不仅计算了准确度,
- 并且传入了一个test_data_y数组参数用于存储预测结果,方便后续的查看。
-
- # 训练集准确度
- def train_accuracy(probs_train, train_data):
- """
- 根据训练数据集来计算训练准确度train_accuracy
- Args:
- probs_train -- 训练数据集的预测结果,调用paddle.infer()来获取
- train_data -- 训练数据集
- Return:
- train_accuracy -- 训练准确度train_accuracy
- """
- train_right = 0
- train_total = len(train_data['label'])
- for i in range(len(probs_train)):
- if float(probs_train[i][0]) > 0.5 and train_data['label'][i] == 1:
- train_right += 1
- elif float(probs_train[i][0]) < 0.5 and train_data['label'][i] == 0:
- train_right += 1
- train_accuracy = (float(train_right) / float(train_total)) * 100
- return train_accuracy
-
- # 测试集准确度
- def test_accuracy(probs_test, test_data, test_data_y):
- """
- 根据测试数据集来计算测试准确度test_accuracy
- Args:
- probs_test -- 测试数据集的预测结果,调用paddle.infer()来获取
- test_data -- 测试数据集
- test_data_y -- 数组参数用于存储预测结果,方便后续的查看
- Return:
- test_accuracy -- 测试准确度test_accuracy
- """
- test_right = 0
- test_total = len(test_data['label'])
- for i in range(len(probs_test)):
- if float(probs_test[i][0]) > 0.5:
- test_data_y.append(1)
- if test_data['label'][i] == 1:
- test_right += 1
- elif float(probs_test[i][0]) < 0.5:
- test_data_y.append(0)
- if test_data['label'][i] == 0:
- test_right += 1
-
- test_accuracy = (float(test_right) / float(test_total)) * 100
- return test_accuracy
-
- 6.调用上述两个函数并输出
- # 计算train_accuracy和test_accuracy
- test_data_y = []
- print("train_accuracy: {} %".format(train_accuracy(probs_train, train_data)))
- print("test_accuracy: {} %".format(test_accuracy(probs_test, test_data, test_data_y)))
- #train_accuracy: 98.5645933014 %
- #test_accuracy: 70.0 %
-
- 5.因为数据集和逻辑回归模型的限制,并且没有加入其它优化方式,所以70%的测试集准确率已经是相当好的结果,如果你也得到相似的结果,
- 大约98%的训练集准确率和70%的测试集准确率,那么恭喜你到目前为止的工作都很棒,你已经配置了不错的模型和参数。
- 当然你可以返回去调整一些参数,例如learning_rate/batch_size/num_passes,或者参考官方PaddlePaddle文档来修改你的模型,
- 尝试去犯错或者通过调参来得到更好的结果都能帮助你熟悉深度学习以及PaddlePaddle框架的使用!
-
- 6.学习曲线
- 接下来我们利用之前保存的costs数据来输出成本的变化情况,利用学习曲线对模型进行分析。
- 可以看到图中成本在刚开始收敛较快,随着迭代次数变多,收敛速度变慢,最终收敛到一个较小值。
- costs = np.squeeze(costs)
- plt.plot(costs)
- plt.ylabel('cost')
- plt.xlabel('iterations (per hundreds)')
- plt.title("Learning rate = 0.00002")
- plt.show()
- 7.接下来,利用之前保存的测试结果test_data_y来对测试数据集中的单个图片进行预测,通过index来选择一张图片,看看你的模型是不是正确预测了这张图片吧!
- # Example of a picture that was wrongly classified.
- index = 14
- plt.imshow((np.array(test_data['image'][index])).reshape((64, 64, 3)))
- print ("y = " + str(test_data_y[index]) + ", you predicted that it is a \"" +
- classes[test_data_y[index]].decode("utf-8") + "\" picture.")
- #y = 0, you predicted that it is a "non-cat" picture.
- 8.总结
- 通过这个练习我们应该记住:
- 1.数据预处理是训练模型之前的必需工作,有时候理解数据并做好预处理工作比构建模型需要更多时间和耐心!
- 2.PaddlePaddle训练模型的基本步骤:
- 1.初始化
- 2.配置网络结构和设置参数:
- 1.定义成本函数cost
- 2.创建parameters
- 3.定义优化器optimizer
- 3.定义event_handler
- 4.定义trainer
- 5.开始训练
- 6.预测infer()并输出准确率train_accuracy和test_accuracy
- 3.练习中的许多参数可以作调整,例如修改学习率会对模型结果产生很大影响,大家可以在本练习或者后面的练习中多做些尝试。
-
- 至此Logistic回归模型的训练工作完成,我们发现在使用PaddlePaddle进行模型配置和训练的过程中不用考虑参数的初始化、成本函数、激活函数、梯度下降、
- 参数更新和预测等具体细节,只需要简单地配置网络结构和trainer即可,并且PaddlePaddle提供了许多接口来改变学习率、成本函数、
- 批次大小等许多参数来改变模型的学习效果,使用起来更加灵活,方便测试,在之后的练习中,我们会对PaddlePaddle更加熟悉。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。