神经网络:线性回归

智能系统课程关于神经网络的第一部分,讲解了权重优化问题,介绍了线性函数的闭式解和梯度下降法。

线性回归模型

对于线性函数

f_{w_0, w_1}(x) = w_1x + w_0 = y

确定w_0w_1,使得f_{w_0, w_1}(x)更好地拟合已有的数据。


损失函数(Loss Function)

损失函数可以采用和方差或均方差的形式,教授在课件中使用的是和方差形式,在代码中用了均方差。

和方差:

Loss(f_w) = \sum\limits_{i = 1}^N (y_i - f_w(x_i))^2

均方差:

Loss(f_w) = \frac{1}{n} \sum\limits_{i = 1}^N (y_i - f_w(x_i))^2

另外通常引入常系数\frac{1}{2}来抵消求导之后的系数2,方便计算。教授在课件中使用了该常系数但代码中没有,导致代码中带有一项2.0/n。

带有系数\frac{1}{2}的和方差损失函数:

Loss(f_w) = \frac{1}{2} \sum\limits_{i = 1}^N (y_i - f_w(x_i))^2

带有系数\frac{1}{2}的均方差损失函数:

Loss(f_w) = \frac{1}{2n} \sum\limits_{i = 1}^N (y_i - f_w(x_i))^2

添加系数或使用平均值对参数的优化没有影响。

损失函数的梯度

梯度(Gradient)

\nabla f_{w_0, w_1}(x) = \begin{pmatrix}\frac{\partial \mathcal{f}}{\partial w_0}\\ \frac{\partial \mathcal{f}}{\partial w_1}\end{pmatrix}

梯度是损失函数关于需优化的参数的偏导数构成的向量。

梯度的方向指向当前位置斜率最大的方向。

Für jede Stelle (x,y) zeigt der Vektor in die Richtung des steilsten Anstiegs von f.

当梯度为零向量时,当前位置是f的局部极小值。

Ist ∇f an einer Stelle (x,y) der Nullvektor, so ist (x,y) ein Optimum von f.

损失函数的梯度计算

以课件中的带系数和方差损失函数为例:

\frac{\partial Loss(f_w)}{\partial w_1} = -\sum_i (y_i - (w_1x_i + w_0)) * x_i
\frac{\partial Loss(f_w)}{\partial w_0} = -\sum_i (y_i - (w_1x_i + w_0))


两种优化方法

闭式解(Closed-form)

直接令\nabla f = 0,求w_0w_1

缺点:只适合简单的情况,对于线性函数的求解比较容易,而对于许多非线性方程难以求解,此时使用梯度下降法。

梯度下降(Gradient Descent)

更新公式(Update Form)

\theta_j := \theta_j - \alpha \frac{\partial}{\partial \theta_j} J(\theta_0, \theta_1)

其中:

  • \alpha是学习率(learning rate)
  • \theta_0, \theta_1是需要优化的参数,可以有很多个
  • \frac{\partial}{\partial \theta_j} J(\theta_0, \theta_1)是损失函数关于当前参数的偏导数

更新公式的意义是,以-\Delta向梯度为零的位置逐步靠近,步长为\alpha

因此,w_0w_1的更新公式可以写成:

w_1 := w_1 + \alpha \sum_i (y_i - f_w(x_i)) * x_i
w_0 := w_0 + \alpha \sum_i (y_i - f_w(x_i))

教授给出的课件中似乎有些问题,在对损失函数计算偏导数之后使用加号是没有问题的,但是计算之前以\frac{\partial f_w(x)}{\partial w}书写梯度时应该使用减号。

更新公式不是推导出来,而是定义出来的。至于为什么使用减号,我参考了知乎上的一个回答:梯度下降的参数更新公式是如何确定的? – 老董的回答

递归下降法程序

实际程序中需要注意两个参数的设置:\alpha和迭代次数(Iteration/Epochen)。

\alpha过小时,迭代次数需要很大才能找到局部最优解;
\alpha过大时,一次迭代可能就越过了最优解,无法收敛。

import math
import random
import matplotlib.pyplot as plt

num = 50
sizes = random.sample(range(50, 200), num)
prices = [size*(1000+offset) for (size,offset) in zip(sizes, random.sample(range(1000, 3000), num))]

def f(x):
  return w1*x + w0

def loss():
  return (1.0/n) * sum([math.pow(y-f(x), 2) for (x,y) in zip(sizes, prices)]) 

n = len(sizes)
w0,w1=0.0,0.0
epochs = 10
alpha = 0.00001
x1,x2 = min(sizes), max(sizes)
l=[]
for i in range(epochs):
  plt.plot([x1,x2], [f(x1),f(x2)], color='red')  # regression line
  l.append((w0,w1,int(loss()/1000000000)))
  w0 += alpha*(2.0/n)*sum([  (y-f(x)) for (x,y) in zip(sizes, prices)])
  w1 += alpha*(2.0/n)*sum([x*(y-f(x)) for (x,y) in zip(sizes, prices)])
  # 这里系数是2/n,说明用的是均方差,且没有添加系数1/2

print("f(x)= %.2f x + %.2f"%(w1,w0))

plt.scatter(sizes, prices) 
plt.ylabel("Flat Price in EUR")
plt.xlabel("Flat Size in $m^2$")
plt.show()

plt.plot([x1,x2], [f(x1),f(x2)], color='red')  # regression line
plt.scatter(sizes, prices) 
plt.ylabel("Flat Price in EUR")
plt.xlabel("Flat Size in $m^2$")
plt.show()

l2=[]
for w0 in range(0,50,10):
  for w1 in range(1000,5000,100):
    l2.append([w0,w1,int(loss()/1000000000)])

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
w0s,w1s,losss = zip(*l)
ax.scatter(w1s, w0s, losss, label="Gradient descent", marker="o")
w0s,w1s,losss = zip(*l2)
ax.scatter(w1s, w0s, losss,  marker=".")
ax.set_xlabel('$w_0$')
ax.set_ylabel('$w_1$')
ax.set_zlabel('Loss in $10^9$', rotation=90)
ax.legend()
plt.tight_layout()
plt.show()




参考内容

损失函数为什么用平方形式(二)

梯度下降的参数更新公式是如何确定的? – 老董的回答

Lecture 2 – Linear Regression and Gradient Descent | Stanford CS229: Machine Learning (Autumn 2018)

发表评论

您的电子邮箱地址不会被公开。