PS:参考书籍《Python机器学习算法》——赵志勇 @电子工业出版社
我们从最终代码反过来看计算过程
1.首先导入模块
import numpy as np
2.然后下面是主训练函数
def lr_train_bgd(feature, label, maxCycle, alpha):
'''
利用梯度下降法训练LR模型
:param feature: 特征矩阵(mat)
:param label: 标签矩阵(mat)
:param maxCycle: 最大迭代次数(int)
:param alpha: 学习率(float)
:return: w:权重矩阵(mat)
'''
n = np.shape(feature)[1] # 特征个数(多少列)
w = np.mat(np.ones((n, 1))) # 初始化权重矩阵
for i in range(maxCycle):
h = sigmoid(feature * w) # 计算Sigmoid值
err = label - h # 误差值矩阵等于标签矩阵减去Sigmoid值矩阵(这里计算的并非是误差率)
if (i + 1) % 100 == 0:
print("\t----------迭代次数:{},训练误差率为:{}".format(i + 1, error_rate(h, label)))
# 权重修正,权重初始化时全部值都是0, 通过不断迭代依次修正
w = w + alpha * feature.T * err # 修正公式为:原权重矩阵+学习率*特征转置矩阵*误差值矩阵
return w
①获取特征矩阵feature的特征个数,也就是这个矩阵的列数,赋值给n
②初始化权重矩阵,最开始的权重矩阵w设置成n行1列的全零矩阵,赋值给w
③以给定最大迭代次数来循环
计算当前w矩阵乘以feature矩阵的结果的sigmoid值矩阵,赋值给h
计算当前label与h的差,此为误差值矩阵,赋值给err
每循环100次打印一次当前状态
修正权重矩阵,修改公式为:新权重矩阵 = 原权重矩阵 + 学习率 * 特征矩阵的转置 * 误差矩阵
④返回最终权重矩阵
3.主训练函数中出现的求sigmoid值矩阵的自定义函数
def sigmoid(x):
'''
sigmoid函数
:param x: 特征矩阵*权重矩阵 产生的结果矩阵(mat)
:return: sigmoid值矩阵(mat)
'''
return 1 / (1 + np.exp(-x))
数学上的sigmoid函数公式为,因此有此此定义函数中出现的公式。
4.主训练函数中出现的计算误差率的自定义函数error_rate
def error_rate(h, label):
'''
损失函数,计算当前的损失函数值
:param h: 预测值矩阵(mat)
:param label: 实际值矩阵(mat)
:return: err/m:错误率(float)
'''
m = np.shape(h)[0] # 获得预测值个数
sum_err = 0.0 # 初始化误差和
for i in range(m): # 循环m次
if h[i, 0] > 0 and (1 - h[i, 0]) > 0: # 若h[i,0]大于0并且1-h[i,0]也大于0,其实也就是0 < h[i,0] < 1就符合要求。
# 新的误差和等于原自身减去下面这个公式
sum_err -= (label[i, 0] * np.log(h[i, 0]) + (1 - label[i, 0]) * np.log(1 - h[i, 0]))
else:
# 否则新的误差和无变化,此步其实可以省略
sum_err -= 0
'''
所以上面的for循环其实可以简化成下面这样:
for i in range(m):
if h[i, 0] > 0 and h[i, 0] < 1:
sum_err -= label[i, 0] * np.log(h[i, 0]) + (1 - label[i, 0]) * np.log(1 - h[i, 0])
'''
return sum_err / m
①获得预测值个数,也就是输入参数h的列数(h为m行1列的矩阵),赋值给m
②初始化误差和(零化)
③以预测值个数m作为循环次数
如果当前循环次数i下的h[i,0]这个值大于0小于1
则误差和减去以下公式的值 (label[i, 0] * np.log(h[i, 0]) + (1 - label[i, 0]) * np.log(1 - h[i, 0]))
④返回误差和除以预测值个数m的值
主要难点:
求误差率函数中,在符合条件下的误差和减去的那条公式比较难懂,不过既然是给定的,可以硬记下来没关系。
另外,主训练函数中的输入参数:最大迭代次数以及学习率的确定也是很值得深思的一个问题。在此例题中,本书作者最终调用时,输入的最大迭代次数为1000次,学习率为0.01
5.导入数据函数
def load_data(filename):
'''
:param filename:文件名(string)
:return: feature:特征矩阵(mat)
label:标签矩阵(mat)
'''
feature = []
label = []
with open(filename, "r", encoding="utf-8") as f:
for line in f.readlines():
feature_tmp = []
label_tmp = []
lines = line.strip("\t")
feature_tmp.append(1) # 偏置项
for i in range(len(lines) - 1):
feature_tmp.append(float(lines[i]))
label_tmp.append(float(lines[-1]))
feature.append(feature_tmp)
label.append(label_tmp)
return np.mat(feature), np.mat(label)
其实说白了就是将txt文档里面的数据转换成对应的矩阵而已,输出的是特征矩阵和标签矩阵,唯一难懂点的地方就是每一行都会有一个偏置项1。
如果是我写这个读取数据的自定义函数,我可能会这么写
import os
import pickle
def load_data(dataname, filename):
'''
:param dataname:已处理数据文件名(string)
:param filename:未处理数据文件名(string)
:return: feature:特征矩阵(mat)
label:标签矩阵(mat)
'''
if os.path.exists(dataname):
with open(dataname, "rb") as f:
data = pickle.load(f)
return data[:, :-1], data[:, -1]
feature_data = []
label_data = []
with open(filename, "r", encoding="utf-8") as f:
for line in f.readlines():
feature_tmp = []
label_tmp = []
lines = line.strip("\t")
feature_tmp.append(1) # 偏置项
for i in range(len(lines) - 1):
feature_tmp.append(float(lines[i]))
label_tmp.append(float(lines[-1]))
feature_data.append(feature_tmp)
label_data.append(label_tmp)
feature = np.mat(feature_data)
label = np.mat(label_data)
data = np.hstack((feature, label))
with open(dataname, "wb") as f:
pickle.dump(data, f)
return feature, label
传入的参数有两个,一个是已处理数据的文件名,一个是未处理数据的文件名
先是判断如果存在已处理过的数据的文件,直接读取该文件即可,就没必要每次都重复去处理这些数据了。
如果没有,就读取未处理数据文件,进行处理后,保存成已处理数据文件,顺带输出。
pickle模块的好处是可以实现任意数据类型的数据的本地读写,什么格式保存的,读进来就已经是原先的格式,不需要再做任何转换处理,唯一不好的地方就是,该模块用的是二进制模式存取数据,所以不支持人眼阅读。因为在我们看来,这些数据就成了乱码。
6.保存数据函数(将权重矩阵保存到本地)
def save_model(filename, w):
'''
:param filename:需要保存成的文件名(string)
:param w: 需要保存的LR模型的权重矩阵(mat)
:return:
'''
with open(filename, "wb") as f:
pickle.dump(w, f)
7.主代码段
if __name__ == '__main__':
# 1.加载训练数据
feature, label = load_data("data.mat", "data.txt")
# 2.训练LR模型
w = lr_train_bgd(feature, label, 1000, 0.01)
# 3.保存最终模型
save_model("model.mat", w)
以上,便是基于梯度下降法训练的LR模型,下一篇文章继续编辑如何利用训练出来的模型进行数据预测。