简介

在统计学中,线性回归(Linear regression)是利用称为线性回归方程的最小二乘函数对一个或多个自变量和因变量之间关系进行建模的一种回归分析。这种函数是一个或多个称为回归系数的模型参数的线性组合。只有一个自变量的情况称为简单回归,大于一个自变量情况的叫做多元回归。

在线性回归中,数据使用线性预测来建模,并且未知的模型参数也是通过数据来估计的。这些模型被叫做线性模型。

模型

以预测房价为例,假设价格指取决于房屋的两个因素:面积(平方米)和房龄(年)。

设房屋的面积是$x_1$,房龄是$x_2$,出售价格是$y$,建立基于输入$x_1$和$x_2$来计算输出$y$的表达式,也就是模型(model)。线性回归假设输出与各输入之间是线性关系:

$$\hat{y}=x_1w_1+x_2w_2+b$$

其实$w_1$,$w_2$是权重(weight),b是偏置(bias),均为标量。它们是线性回归模型的参数(parameter)。模型是输出$\hat{y}$是线性回归对真实价格$y$的预测或估计。通常允许有一定误差。

模型训练

然后需要通过数据来寻找特定的模型参数数值,使得模型在数据上的误差尽可能小。这个过程叫做模型训练(Model training)。

训练数据

通常收集一系列真实的数据,在这个数据上面寻找模型参数来最小化模型的预测价格与真实价格的误差。在机器学习中,该数据被称为训练数据集(training data set)或者训练集(training set),一栋房子的价格被称为一个样本(sample),其真实的价格叫做标签(label),用来预测标签的两个因素叫做特征(feature),特征用来表征样本的特点。

价格样本数为$n$,索引为$i$的样本特征为$\sideset{}{_1^{(i)}}x$,$\sideset{}{_2^{(i)}}x$,标签为$\hat{y}$。对索引为$i$的房屋,线性回归模型的预测表达式为

$$\hat{y}^{(i)}=\sideset{}{_1^{(i)}}xw_1+\sideset{}{_2^{(i)}}xw_2+b$$

损失函数

在模型训练中,我们需要衡量价格预测值与真实值之间的误差。通常我们会选取一个非负数作为误差,且数值越小表示误差越小。一个常用的选择是平方函数。它在评估索引为 i 的样本误差的表达式为

$$l^{(i)}(w_1,w_2,b)=\frac{1}{2}(\hat{y}^{(i)}-y^{(i)})^2$$

其中常数 1/2 使得对平方项求导后的常数系数为 1,这样在形式上稍微简单一些。显然,误差越小表示预测价格与真实价格越相近,且当二者相等时误差为 0。给定训练数据集,这个误差只与模型参数相关,因此我们将它记为以模型参数为参数的函数。机器学习里,我们将衡量误差的函数称为损失函数(loss function)。这里使用的平方误差函数也被称为平方损失(square loss)。通常,我们用训练数据集中所有样本误差的平均来衡量模型预测的质量,即

$$l(w_1,w_2,b)=\frac{1}{n}\sum_{i=1}^nl^{(i)}(w_1,w_2,b)$$

$$=\frac{1}{n}\sum_{i=1}^n\frac{1}{2}(\sideset{}{_1^{(i)}}xw_1+\sideset{}{_2^{(i)}}xw_2+b-y^{(i)})^2$$

在模型训练中,我们希望找出一组模型参数,来使得训练样本平均损失最小:

$$w_1’,w_2’ ,b’ = argmin_{w_1,w_2,b}l(w_1,w_2,b)​$$

优化算法

当模型和损失函数形式较为简单时,上面的误差最小化问题的解可以直接用公式表达出来。这类解叫做解析解(analytical solution)。本节使用的线性回归和平方误差刚好属于这个范畴。然而,大多数深度学习模型并没有解析解,只能通过优化算法有限次迭代模型参数来尽可能降低损失函
数的值。这类解叫做数值解(numerical solution)。
在求数值解的优化算法中,小批量随机梯度下降(mini-batch stochastic gradient descent)在深度学习中被广泛使用。它的算法很简单:我们先选取一组模型参数的初始值,例如随机选取;接下来对参数进行多次迭代,使得每次迭代都可能降低损失函数的值。在每次迭代中,我们先随机均匀采样一个由固定数目训练数据样本所组成的小批量(mini-batch)B;然后求小批量中数据样本的平均损失有关模型参数的导数(梯度);最后用此结果与预先设定的一个正数的乘积作为模型参数在本次迭代的减小量。

模型预测

模型训练完成以后,可以得到一个对最优解的近似。然后可以用模型来估算训练数据集以外的任意一栋房屋的房价。这里估算也叫模型预测、模型推断和模型测试。

我们应尽可能采用矢量计算,以提升计算效率。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
from IPython import display
from mxnet import nd, autograd
import random
from matplotlib import pyplot as plt

# 生成数据集 加入一个服从N(0,0.01)的噪点
num_input = 2
num_example = 1000
true_w = [2, -3.4]
true_b = 4.2

features = nd.random.normal(scale=1, shape=(num_example, num_input))
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += nd.random.normal(scale=0.01, shape=labels.shape)

# 显示图像
# def use_svg_display():
# display.set_matplotlib_formats('svg')
#
# def set_figsize(figsize=(3.5, 2.5)):
# use_svg_display()
# # 设置图的尺寸。
# plt.rcParams['figure.figsize'] = figsize
# set_figsize()
# plt.show(plt.scatter(features[:, 1].asnumpy(), labels.asnumpy(), 1))

def data_iter(batch_size, features, labels):
num_example = len(features)
indices = list(range(num_example))
# 样本读取随机
random.shuffle(indices)
for i in range(0, num_example, batch_size):
j = nd.array(indices[i: min(i + batch_size, num_example)])
# 根据索引返回元素
yield features.take(j), labels.take(j)

# 初始化模型参数
w = nd.random.normal(scale=0.01, shape=(num_input, 1))
b = nd.zeros(shape=(1,))

# 创建它们的梯度
w.attach_grad()
b.attach_grad()

# 定义模型
def linreg(x, w, b):
return nd.dot(x, w) + b

# 定义损失函数
def squared_loss(y_hat, y):
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

# 定义优化算法
def sgd(params, lr, batch_size):
for param in params:
param[:] = param - lr * param.grad / batch_size


# 训练模型
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
batch_size = 10

# for x, y in data_iter(batch_size, features, labels):
# print(x, y)

#迭代周期 num_epochs
for epoch in range(num_epochs):
# 一个迭代周期
for x,y in data_iter(batch_size, features, labels):
with autograd.record():
# l为损失
l = loss(net(x, w, b), y)
# 小批量的损失对模型参数求梯度
l.backward()

sgd([w, b], lr, batch_size)
tran_l = loss(net(features, w, b), labels)
print("epoch %d, loss %f" % (epoch + 1, tran_l.mean().asnumpy()))

print("train_w: ")
print(w)
print("true_w: ")
print(true_w)
print("train_b: ")
print(b)
print("true_b: ")
print(true_b)

TensorFlow实现线性模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import tensorflow as tf
import numpy as np

# 创建数据集
x_data = np.random.rand(100).astype(np.float32)
y_data = x_data * 0.1 + 0.3

# 权重和偏置初始化
Weights = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
biases = tf.Variable(tf.zeros([1]))

# 线性模型
y = Weights * x_data + biases

# 损失和优化器
loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)

# 训练模型
init = tf.initialize_all_variables()

sess = tf.Session()
sess.run(init)

for step in range(201):
sess.run(train)
if step % 20 == 0:
print(step, sess.run(Weights), sess.run(biases))