2022.6.8
官方文档网址:
一、机器学习、人工智能、深度学习的关系
人工智能>机器学习>深度学习
图1 官方关系图
机器学习的实现:(1)训练、预测。(2)模型假设、评价函数、优化算法
深度学习:基于神经网络算法。
人工神经网络:
神经元
多层连接
向前计算
计算图
二、利用神经网络模型的波士顿房价预测实践
1.线性回归模型
线性函数
线性回归模型是房价和各个影响因素之间的线性关系方程式。
xj为各个影像因素,wi为各个因素的权重,b为各个因素的偏置。
损失函数
2.深度学习模型

数据处理:数据导入、数据形状变换、数据集划分、数据归一化处理、封装函数。
3.波士顿房价数据准备
文件的下载:
wget https://archive.ics.uci.edu/ml/machine-learning-databases/housing/housing.data -O housing.data
数据结构:
4.波士顿房价预测
(1)数据处理:
①数据导入:
# 导入需要用到的packageimport numpy as npimport json# 读入训练数据datafile = 'housing.data'data = np.fromfile(datafile, sep=' ')
②数据形状变换
读入的数据为一维状态,需要将数据转换为二维数据。
feature_names = [ 'CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE','DIS','RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV' ]feature_num = len(feature_names)data = data.reshape([data.shape[0] // feature_num, feature_num])
③数据集拆分
此处80%的数据作为训练集,20%的数据作为测试集。
ratio = 0.8 #设置抽取的数据集所占的比例offset = int(data.shape[0] * ratio) #生成数据集training_data = data[:offset] #获取训练数据集test_data = data[offset:] #获取测试数据集
④数据归一化处理
# 计算train数据集的最大值,最小值,平均值maximums, minimums, avgs = \training_data.max(axis=0), \training_data.min(axis=0), \training_data.sum(axis=0) / training_data.shape[0]# 对数据进行归一化处理for i in range(feature_num):#print(maximums[i], minimums[i], avgs[i])data[:, i] = (data[:, i] - avgs[i]) / (maximums[i] - minimums[i])
注:
⑤封装数据读取导入处理函数
def load_data():# 从文件导入数据datafile = './work/housing.data'data = np.fromfile(datafile, sep=' ')# 每条数据包括14项,其中前面13项是影响因素,第14项是相应的房屋价格中位数feature_names = [ 'CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', \'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV' ]feature_num = len(feature_names)# 将原始数据进行Reshape,变成[N, 14]这样的形状data = data.reshape([data.shape[0] // feature_num, feature_num])# 将原数据集拆分成训练集和测试集# 这里使用80%的数据做训练,20%的数据做测试# 测试集和训练集必须是没有交集的ratio = 0.8offset = int(data.shape[0] * ratio)training_data = data[:offset]# 计算训练集的最大值,最小值,平均值maximums, minimums, avgs = training_data.max(axis=0), training_data.min(axis=0), \training_data.sum(axis=0) / training_data.shape[0]# 对数据进行归一化处理for i in range(feature_num):#print(maximums[i], minimums[i], avgs[i])data[:, i] = (data[:, i] - avgs[i]) / (maximums[i] - minimums[i])# 训练集和测试集的划分比例training_data = data[:offset]test_data = data[offset:]return training_data, test_data
# 获取数据training_data, test_data = load_data()x = training_data[:, :-1]y = training_data[:, -1:]
# 查看一条x,y数据print(x[0])print(y[0])
(2)模型设计
①向前计算
首先假设一个权值w
w = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, -0.1, -0.2, -0.3, -0.4, 0.0]w = np.array(w).reshape([13, 1])
然后,取一条特征向量与参数向量相乘。
x1=x[0]t = np.dot(x1, w)print(t)#结果:[0.03395597]
之后同样假设一个初始化偏移量b<br />这样就得到了一个线性回归方程的完整输出:z = t + b<br />这个从特征和参数计算输出值的过程称为“向前计算”。<br />**注**:<br />特征向量:数据处理结果x的任意一条数据都称为x的一个特征向量。<br />参数向量:每个x矩阵每个特征的权重所组成的向量w。<br />t = np.dot(x1 , w):np.dot()是矩阵乘法。例:np.dot([1,2,3],[4,5,6])=1*4+2*5+3*6=32<br />**封装向前计算**:<br />将向前计算封装为一个类。
class Network(object):#此函数用以初始化w值(随机生成w值)def __init__(self, num_of_weights):# 设置固定的随机数种子,为了保持程序每次运行生成的随机值相同。np.random.seed(0)#生成随机值self.w = np.random.randn(num_of_weights, 1)#设置b的值为0.0self.b = 0.def forward(self, x):z = np.dot(x, self.w) + self.breturn z
**封装后的调用方法**:
net = Network(13)x1 = x[0]y1 = y[0]z = net.forward(x1)print(z)#结果:[-0.63182506]
(3)训练配置
所谓的训练配置其实就是通过给定的损失函数来计算模型的好坏,从而获取最优解。
对于回归问题,最常用的衡量方法(损失函数)就是均方误差。
Loss(损失函数指标)简记为:L
①损失函数指标的计算

Loss = (y1 - z)*(y1 - z)print(Loss)#结果:[0.39428312]
**损失函数封装**:封装后的损失函数需要添加到Network()类中。
def loss(self, z, y):error = z - ycost = error * errorcost = np.mean(cost)return cost
注:以上计算过程中的w和z值均为随机值,而在机器学习中,w和z的值应为计算得出。故以下过程讲解如何求解w和z的值。
(4)训练过程
①线性回归方法
所谓的训练过程就是找到一个让损失函数指标值尽可能小的w和z,即最优w和z。
此处利用导数求解极值的思想,当斜率为0时,函数值取得极值。
①首先损失函数可写为:
②对b求偏导数,并令结果等于0,求解得:
其中
,
。
③此时将b带入损失函数中,经计算可以得到:
将训练的数据x,y带入上式并求解,即可得到最优的w和z值
*注:以上方法只对线性回归有效。普适性较差。
②梯度下降法
思想:利用数据集依次计算w1,w2…….,w12;
损失函数改写:
预测函数:
梯度的定义与计算:
对L求导计算各个梯度w和b:

x1 = x[0]y1 = y[0] #y1 [-0.00390539]z1 = net.forward(x1) #z1 [-12.05947643]gradient_w0 = (z1 - y1) * x1[0] #gradient_w0 [0.25875126]此时计算的是w0gradient_w1 = (z1 - y1) * x1[1] #此处计算的是w1
# 利用循环来计算ww = []for i in x1:gradient_wi = (z1 - y1) * x1[i]w.append(gradient_wi)print(w) # 输出w
# 计算一个样本gradient_w = (z1 - y1) * x1# 计算多个样本x3samples = x[0:3]y3samples = y[0:3]z3samples = net.forward(x3samples)gradient_w = (z3samples - y3samples) * x3samples# 扩展样本维度z = net.forward(x)gradient_w = (z - y) * x # shape (404, 13)
**计算样本梯度贡献值**:<br />
# axis = 0 表示把每一行做相加然后再除以总的行数gradient_w = np.mean(gradient_w, axis=0)
**w维度变换**:
gradient_w = gradient_w[:, np.newaxis]
**综合计算w梯度**:
z = net.forward(x)gradient_w = (z - y) * xgradient_w = np.mean(gradient_w, axis=0)gradient_w = gradient_w[:, np.newaxis]gradient_w
**计算b梯度**:
gradient_b = (z - y)gradient_b = np.mean(gradient_b)gradient_b
封装numpy计算梯度函数:
def gradient(self, x, y):z = self.forward(x)gradient_w = (z-y)*xgradient_w = np.mean(gradient_w, axis=0)gradient_w = gradient_w[:, np.newaxis]gradient_b = (z - y)gradient_b = np.mean(gradient_b)return gradient_w, gradient_b
**调用封装的函数计算梯度**:
# 调用上面定义的gradient函数,计算梯度# 初始化网络net = Network(13)# 设置[w5, w9] = [-100., -100.]net.w[5] = -100.0net.w[9] = -100.0z = net.forward(x)loss = net.loss(z, y)gradient_w, gradient_b = net.gradient(x, y)gradient_w5 = gradient_w[5][0]gradient_w9 = gradient_w[9][0]print('point {}, loss {}'.format([net.w[5][0], net.w[9][0]], loss))print('gradient {}'.format([gradient_w5, gradient_w9]))
梯度更新:
沿着梯度反方向移动一小步观察损失函数的变化。
# 在[w5, w9]平面上,沿着梯度的反方向移动到下一个点P1# 定义移动步长 etaeta = 0.1# 更新参数w5和w9net.w[5] = net.w[5] - eta * gradient_w5net.w[9] = net.w[9] - eta * gradient_w9# 重新计算z和lossz = net.forward(x)loss = net.loss(z, y)gradient_w, gradient_b = net.gradient(x, y)gradient_w5 = gradient_w[5][0]gradient_w9 = gradient_w[9][0]print('point {}, loss {}'.format([net.w[5][0], net.w[9][0]], loss))print('gradient {}'.format([gradient_w5, gradient_w9]))# point [-99.91499266760042, -99.38615876351922], loss 678.6472185028845# gradient [-0.8556356178645292, -6.0932268634065805]
eta:用于控制沿梯度反方向移动距离的大小,又称为学习效率。
相减:表明参数向反方向移动。
封装梯度更新函数:
def update(self, gradient_w, gradient_b, eta = 0.01):self.w = self.w - eta * gradient_wself.b = self.b - eta * gradient_b
封装Train(变换)函数:
def train(self, x, y, iterations=100, eta=0.01):losses = []for i in range(iterations):z = self.forward(x)L = self.loss(z, y)gradient_w, gradient_b = self.gradient(x, y)self.update(gradient_w, gradient_b, eta)losses.append(L)if (i+1) % 10 == 0:print('iter {}, loss {}'.format(i, L))return losses# 获取数据train_data, test_data = load_data()x = train_data[:, :-1]y = train_data[:, -1:]# 创建网络net = Network(13)num_iterations=1000# 启动训练losses = net.train(x,y, iterations=num_iterations, eta=0.01)# 画出损失函数的变化趋势plot_x = np.arange(num_iterations)plot_y = np.array(losses)plt.plot(plot_x, plot_y)plt.show()
③随机梯度下降法
每次从总的数据集中随机抽取出小部分数据来代表整体,基于这部分数据计算梯度和损失来更新参数。
mini-batch:每次迭代时抽取出来的一批数据被称为一个mini-batch。
batch_size:一个mini-batch所包含的样本数目称为batch_size。
epoch:当程序迭代的时候,按mini-batch逐渐抽取出样本,当把整个数据集都遍历到了的时候,则完成了一轮训练,也叫一个epoch。启动训练时,可以将训练的轮数num_epochs和batch_size作为参数传入。
④总结:
训练代码:
import numpy as npclass Network(object):def __init__(self, num_of_weights):# 随机产生w的初始值# 为了保持程序每次运行结果的一致性,此处设置固定的随机数种子#np.random.seed(0)self.w = np.random.randn(num_of_weights, 1)self.b = 0.def forward(self, x):z = np.dot(x, self.w) + self.breturn zdef loss(self, z, y):error = z - ynum_samples = error.shape[0]cost = error * errorcost = np.sum(cost) / num_samplesreturn costdef gradient(self, x, y):z = self.forward(x)N = x.shape[0]gradient_w = 1. / N * np.sum((z-y) * x, axis=0)gradient_w = gradient_w[:, np.newaxis]gradient_b = 1. / N * np.sum(z-y)return gradient_w, gradient_bdef update(self, gradient_w, gradient_b, eta = 0.01):self.w = self.w - eta * gradient_wself.b = self.b - eta * gradient_bdef train(self, training_data, num_epochs, batch_size=10, eta=0.01):n = len(training_data)losses = []for epoch_id in range(num_epochs):# 在每轮迭代开始之前,将训练数据的顺序随机打乱# 然后再按每次取batch_size条数据的方式取出np.random.shuffle(training_data)# 将训练数据进行拆分,每个mini_batch包含batch_size条的数据mini_batches = [training_data[k:k+batch_size] for k in range(0, n, batch_size)]for iter_id, mini_batch in enumerate(mini_batches):#print(self.w.shape)#print(self.b)x = mini_batch[:, :-1]y = mini_batch[:, -1:]a = self.forward(x)loss = self.loss(a, y)gradient_w, gradient_b = self.gradient(x, y)self.update(gradient_w, gradient_b, eta)losses.append(loss)print('Epoch {:3d} / iter {:3d}, loss = {:.4f}'.format(epoch_id, iter_id, loss))return losses# 获取数据train_data, test_data = load_data()# 创建网络net = Network(13)# 启动训练losses = net.train(train_data, num_epochs=50, batch_size=100, eta=0.1)# 画出损失函数的变化趋势plot_x = np.arange(len(losses))plot_y = np.array(losses)plt.plot(plot_x, plot_y)plt.show()
⑤保存模型:
np.save('w.npy', net.w)np.save('b.npy', net.b)
三、飞桨深度学习开源开放平台
1.深度度学习框架设计思路
建模者只实现模型中个性化的部分,这样可以在“节省投入”和“产出强大”之间达到一个平衡。
