5.3 随机梯度下降法
我们首先来看随机梯度下降法的运算顺序。前面我们已经讲了这种算法的学习逻辑。它对整个数据集要循环好几次。每次计算都利用上一条的数据得到的最优权重作为基准,在此基础上再使用梯度下降来得到新的最优权重。所以对于我们上面考虑的成本和售价问题,就可以分段写为如下的几个部分。甚至可以建几个py文件,分别对应不同的功能,最后用一个main函数把它们都串在一起。这样操作的话,代码会比较干净。
第一步是数据处理,正如我们以前强调的,最好要把数据处理成正规化数据,处理代码如下:
import numpy as np
cost_of_material = np.array([38.3, 35, 31.2, 43.2, 44.2, 41.2])
cost_of_sales = np.array([12.3, 11.2, 10.1, 9.2, 9.1, 9.6])
cost_of_human = np.array([22,21.3,23.7,23.2,20.5,24.2])
cost_of_product = np.array([7.2, 7.8, 8.3, 8.6, 8.8, 9.3])
sell_price = np.array([100.1, 102, 99.2, 101.2, 103.8, 100.5])
# 数据正规化,由于都是钱计价的,所以可以用同一个单位来正规化,比如这里使用100, 也可以不正规化,这里使用是为了提示你必须先考虑特征的正规化。
cost_of_material /= 100
cost_of_sales /= 100
cost_of_human /= 100
cost_of_product /= 100
sell_price /= 100
# 学习速率
learning_rate = np.array([0.5,0.5,0.5,0.5])
第二步是将随机梯度下降的算法部分全部实现为一个rand_grad类。它可以写成如下的形式:
class rand_grad:
def __init__(self,inputs,outputs,weights):
self.inputs = inputs
self.weights = weights
self.outputs = outputs
# 预测函数
def neural_network(self):
prediction = self.inputs.dot(self.weights)
return prediction
# 代价函数
def error_function(self):
prediction = self.neural_network()
return pow(prediction - self.outputs,2)
# 梯度下降乘子
def grad_descent_multiplier(self):
multiplier = (self.neural_network() - \
self.outputs)*inputs
return multiplier
# 运算停止控制条件
def stop_condition(self,times):
if (self.error_function()>1e-12 or times<50):
return True
else:
return False
第三步是权重和数据的矩阵化预处理,方便使用矩阵的切片以及矩阵乘法,我们在上面的类中实现的也是矩阵算法,所以调用矩阵型的数据是比较方便的。
# 预设权重
weights = np.array([0.6,0.2,0.8,0.9])
# 转换成矩阵形式
raw_data = np.vstack((cost_of_material,cost_of_sales,cost_of_human,cost_of_product))
raw_data = raw_data.T
print(raw_data[0])
第四步是使用循环嵌套来实现随机梯度下降,这种算法基本没有使用矩阵计算的方便性,所以使用循环嵌套是随机梯度下降的一个非常重要的特征。在批处理梯度下降中,就把循环嵌套改写成了矩阵算法,运算效率大大提高。
# 当误差小于10的-10次方,即(1e-10),即停止运算。
for ergo in range(30): #遍历30次数据集
error_list = []
for item_index in range(len(raw_data)):
# 从第一条数据条开始循环
inputs = raw_data[item_index]
outputs = sell_price[item_index]
weights = weights
engine = rand_grad(inputs,outputs,weights)
第五步是嵌套上每一条数据的梯度下降循环,即:
times = 0
while engine.stop_condition(times):
# 此处为梯度下降
times += 1
gdm = list(engine.grad_descent_multiplier())
if min(np.abs(gdm)) > max(np.abs(weights)):
gdm /= 10*min(np.abs(gdm))/max(np.abs(weights))
# 下降因子相对于权重过大,此时应将它缩小,否则极易引起误差发散。
factor = np.diag(gdm)
weights -= np.matmul(learning_rate,factor)
# 将误差记录下来,放到误差列表中
error_list.append(engine.error_function())
我们可以跟随每一次循环数据集,看最终的权重以及整体误差变化的情形。代码如下:
print("权重变化过程:", weights)
print("整体误差变化过程:", sum(error_list))
综合所有的步骤,整体代码为:
import numpy as np
cost_of_material = np.array([38.3, 35, 31.2, 43.2, 44.2, 41.2])
cost_of_sales = np.array([12.3, 11.2, 10.1, 9.2, 9.1, 9.6])
cost_of_human = np.array([22,21.3,23.7,23.2,20.5,24.2])
cost_of_product = np.array([7.2, 7.8, 8.3, 8.6, 8.8, 9.3])
sell_price = np.array([100.1, 102, 99.2, 101.2, 103.8, 100.5])
# 数据正规化,由于都是钱计价的,所以可以用同一个单位来正规化,比如这里使用100, 也可以不正规化,这里使用是为了提示你必须先考虑特征的正规化。
cost_of_material /= 100
cost_of_sales /= 100
cost_of_human /= 100
cost_of_product /= 100
sell_price /= 100
learning_rate = np.array([0.5,0.5,0.5,0.5])
class rand_grad:
def __init__(self,inputs,outputs,weights):
self.inputs = inputs
self.weights = weights
self.outputs = outputs
# 预测函数
def neural_network(self):
prediction = self.inputs.dot(self.weights)
return prediction
# 代价函数
def error_function(self):
prediction = self.neural_network()
return pow(prediction - self.outputs,2)
# 梯度下降乘子
def grad_descent_multiplier(self):
multiplier = (self.neural_network() - \
self.outputs)*inputs
return multiplier
# 运算停止控制条件
def stop_condition(self,times):
if (self.error_function()>1e-10 or times<50):
return True
else:
return False
# 预设权重
weights = np.array([0.6,0.2,0.8,0.9])
# 转换成矩阵形式
raw_data = np.vstack((cost_of_material,cost_of_sales,cost_of_human,cost_of_product))
raw_data = raw_data.T
print(raw_data[0])
# 当误差小于10的-10次方,即(1e-10),即停止运算。
for ergo in range(30): #遍历30次数据集
error_list = []
for item_index in range(len(raw_data)):
# 从第一条数据条开始循环
inputs = raw_data[item_index]
outputs = sell_price[item_index]
weights = weights
engine = rand_grad(inputs,outputs,weights)
times = 0
while engine.stop_condition(times):
# 此处为梯度下降
times += 1
gdm = list(engine.grad_descent_multiplier())
if min(np.abs(gdm)) > max(np.abs(weights)):
gdm /= 10*min(np.abs(gdm))/max(np.abs(weights))
# 下降因子相对于权重过大,此时应将它缩小,否则极易引起误差发散。
factor = np.diag(gdm)
weights -= np.matmul(learning_rate,factor)
error_list.append(engine.error_function())
print("权重变化过程:", weights)
print("整体误差变化过程:", sum(error_list))