QQ:3020889729 小蔡
本文可供学习,有什么疑问可以联系。
模型思路
线性模型
1.ω是权重向量/矩阵
2.x 是所有的输入
利用矩阵乘法,就得到多个输入对应的输出。
损失函数
最小化L的值,得到接近最优的解:
梯度下降优化公式
梯度计算:
应用梯度进行优化:
算法结构
- 构建参数初始化部分 – 初始化模型开始前的权重w和偏置b。
- 构建损失计算部分–计算输入对应的输出以及loss,得到相应梯度。
- 整和参数初始化以及loss计算部分构建完成的训练过程。
参数初始化
根据输入参数的个数,初始化等维度大小的权重w——即一个输入有n个参数(特征),那么对应n个权重w;同时加上一个偏置b,得到一个完整的输出。
这就相当于一个输入为n个参数,输出为1个参数的映射关系(全连接层):
在模型训练中,可以从图中看出来,输出的结果除了与输入参数x有关外,还与w与b密切相关。所以通常在模型训练过程中,参数初始化如果处理得好,那么训练将会事半功倍。
好了,不多说,上一下代码吧:
import numpy as np
# 参数初始化函数
def initialize_params(input_dims, output_dims):
'''
input: input_dims -> 每一次输入数据的维度大小--特征个数或者元素个数
output_dims -> 每一次输出数据对应的维度大小 -- 即输出个数
eg:
X = (x1, x2, x3) -> input_dims = 3
Y = (y1, y2) -> output_dims = 2
return: 权重(w) -> 维度形状:(input_dims, output_dims) == 方便矢量运算(矩阵相关运算)
偏置(b) -> 维度形状: (output_dims, 1) == 维度与输出个数相关
'''
init_w = np.ones((input_dims, output_dims)) # 全一初始化
init_b = np.ones((output_dims, 1))
return init_w, init_b # 返回初始化好的参数
测试结果:
这部分的重点在于,理解矩阵乘法和运用numpy库下的dot矩阵乘法方法计算输出。
例子对应的乘法公式对应如下:
损失函数
计算输入得到的输出与真实值的损失值(误差值),并计算相应的梯度值,为梯度更新优化提供可能。
这部分对线性函数以及矩阵有些了解,理解起来应该不会很难,所以就直接给代码咯:
def linear_loss(x, y, w, b):
'''
input: x -> 输入:(N, feature_num)
y -> 真实值:(N, label_num) # 每一个输入x对应的真实值个数:label_num
w -> 当前的权重: (feature_num, label_num) # label_num 对应模型最终一次输出多少个参数
b -> 当前的偏置: (label_num, 1) # n个输出就n个偏置
return y_pred, loss, dw, db
'''
if isinstance(x, np.ndarray) is False: # 自动判断并转换数据类型
x = np.asarray(x)
assert len(x.shape) == 2, 'Error: input_x\'s shape is false(len(x.shape!=2)), please input the length of x\'s shape == 2 !'
if isinstance(y, np.ndarray) is False:
y = np.asarray(y)
assert len(y.shape) == 2, 'Error: input_y\'s shape is false(len(y.shape!=2)), please input the length of y\'s shape == 2 !'
batch_num = x.shape[0] # 获取一次传入的数据个数(非特征数)
feature_num = x.shape[1] # 获取每个输入的数据所包含的特征数
# 根据之前的公式: f = x*w + b 计算当前w和b下的输出
y_pred = np.dot(x, w) + b
# 计算损失
loss = np.sum(np.sqrt((y_pred - y)**2)) / batch_num
# 参数求梯度
# x.shape: (N, feature_num)
# (y_pred - y).shape: (N, label_num)
dw = np.dot(x.T, (y_pred - y)) / batch_num # w的梯度
# sum((y_pred - y)).shape: (1, label_num)
db = np.sum((y_pred - y)) / batch_num # b的梯度
return y_pred, loss, dw, db
测试结果:
完整的模型训练
这部分简单来说,就是整合初始化和计算损失,再做简单调整即可:
# 训练函数
def linear_train(x, y, learning_rate, epoches):
'''
'''
if isinstance(x, np.ndarray) is False: # 自动判断并转换数据类型
x = np.asarray(x)
assert len(x.shape) == 2, 'Error: input_x\'s shape is false(len(x.shape!=2)), please input the length of x\'s shape == 2 !'
if isinstance(y, np.ndarray) is False:
y = np.asarray(y)
assert len(y.shape) == 2, 'Error: input_y\'s shape is false(len(y.shape!=2)), please input the length of y\'s shape == 2 !'
w, b = initialize_params(x.shape[1], y.shape[1]) # 创建相应的初始化参数
loss_list = [] # 损失参数记录
for i in range(epoches): # 训练epoches轮次
y_pred, loss, dw, db = linear_loss(x, y, w, b)
loss_list.append(loss) # 保存当前损失
w += -learning_rate*dw # 根据输入的学习率进行参数调整
b += -learning_rate*db
if (i+1) % 10000 == 0: # 一万次打印一次训练信息
print('Epoches: {0}, Loss: {1}'.format(i+1, loss))
params = {
'w': w,
'b': b
} # 及时保存训练的参数
grads = {
'dw':dw,
'db':db
} # 及时保存梯度参数
# 训练结束,返回训练损失记录、当前最后轮次的损失,模型参数以及最后一次训练得到的梯度
return loss_list, loss, params, grads
测试结果:
使用训练好的参数进行预测(输出)
这部分就是利用已经训练好的参数进行预测输出(套用计算损失部分的函数结构即可):
代码如下:
def linear_pre(x, y, w, b):
assert x.shape[0] == y.shape[0], 'Error: x.batch_size is not eval(!=) x.batch_size!'
assert w.shape[0] == x.shape[1], 'Error: w is not fit of x!' # 特征数要匹配权重的行数
assert w.shape[1] == y.shape[1], 'Error: w is not fit of y!' # 每一个输入的真实值个数要匹配权重的列数(模型输出个数)
assert b.shape[0] == y.shape[1], 'Error: b is not fit of y!' # 偏置数匹配每一个输入的真实值个数
# 根据之前的公式: f = x*w + b 计算当前w和b下的输出
y_pred = np.dot(x, w) + b
return y_pred
测试结果:
将设计好的模型进行封装(待续)
目的是得到一个可重复使用的线性拟合类,用于拟合一般的线性数据(在功能上不抵已有框架那么方便使用,这里主要是为了阐述线性模型的实现,去熟悉矩阵运算和一般训练步骤和需要的部件–损失函数,模型函数、优化方法(梯度下降))
由于笔者文笔有限,如有表达不清楚,请评论或者联系。