线性回归模型初探
回归问题与分类问题一直是深度学习的两大主要问题,那么回归问题中,最简单的线性回归模型究竟为何物?
案例引入
我们看这么一个案例,假说我们希望对于商品X的价格进行预测,我们简单地认为该商品的价格与原材料价格、当地购买力水平、店铺房租这3个因素有关系,我们简单地认为每个因素对价格的影响比重不同,我们可以先待定这些比重,然后通过大量地数据优化权重,得到一个比较靠谱的权重,如此来预测商品价格
这正是线性回归模型的分析方式,我们根据这一想法,不难写出下面的公式:
上式中,我们加入了偏置b,来调整曲线的位置,获得更好的拟合效果
那么,问题随之而来,我们如何确定出最优的参数呢?
均方误差
高中阶段,我们对于这样的问题就已经有了思考,并且知道了最小二乘法,最小二乘法中,我们提出了均方误差的概念:
上式便是均方误差的表达式,在线性回归问题中,我们就选用这样的误差评估方式,至于选择均方误差的好处,我们以后再说
梯度下降算法
既然有了误差,我们自然而然地就想到要去求误差的最小值,这么多年的数学知识,让我们几乎没有任何思考地就想到,这个函数要么不存在最值,否则,最值一定在某个极值点处取得,直接对每个参数求偏导,并让偏导数等于0,解方程,答案就出来了
然而,我们参数的数量比较多,解这样的方程并不是什么轻松的事情,如果误差函数再复杂一些,难度就更大了,因此这种方法或许适合用于理论计算,但不适合用计算机编程的方式去计算
我们再次思考一下,我们都知道,计算机的很多计算,都是靠近似、逼近这种方式实现的,往这方面去想的话,我们就想到了梯度
再次复习一下,何为梯度?梯度就是曲线增大最陡的方向,数值上则是函数对每一个参数求偏导后组成的向量,那么,问题就好办了,梯度的反方向,不正是递减最快的方向吗?我们只需要向着这一方向走出那么一小步,再更新一下梯度,反复执行这一过程,我们也就在逼近函数的最小值点了
用公式描述这一过程就是:
这里的w代表权重矩阵,对于参数b也是同理操作,而eta则是用于更新的一个很小的值,通常取0.001,0.01这些,它又被叫做学习率
那么如何计算梯度呢?
大家直接记住下面的公式即可,推导也并不困难
像这样,通过不断更新梯度来接近最小值的方法就是梯度下降算法
如果数据集量非常大,我们还有对应的优化方法,下个文章进行讲解
理论模型化
我们把一个“影响因素”叫做一个神经元,x的取值叫输入,而进行预测的计算过程叫做前向传播,结果叫做输出
我们可以得到如下的网络模型:
至此,我们再次梳理一下线性回归的全过程:
1.前向传播(就是算出预测值)
2.计算损失
3.计算梯度
4.更新参数
5.反复执行上述过程直到训练集数据跑完
代码实现(Numpy版本)
import numpy as np
class Network(object):
def __init__(self, num_of_weights):
self.w = np.random.randn(num_of_weights, 1)
self.b = 0.
def forward(self, x):
z = np.dot(x, self.w) + self.b
return z
def loss(self, z, y):
error = z - y
num_samples = error.shape[0]
cost = error * error
cost = np.sum(cost) / num_samples
return cost
def 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_b
def update(self, gradient_w, gradient_b, eta = 0.01):
self.w = self.w - eta * gradient_w
self.b = self.b - eta * gradient_b
def train(self, training_data, num_epochs, batch_size=10, eta=0.01):
n = len(training_data)
losses = []
for iter_id,data_ in enumerate(training_data):
x = data_[:, :-1]
y = data_[:, -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