原理

对于特征集合 $\pmb X$ ,预测值 $\hat y$ ,可用矩阵向量乘法表示为:
$$
\hat{y}=\pmb Xw+b
$$
其中 $X$ 的每一行代表一条数据。

定义损失函数为:
$$
l^{(i)}(w,b)=\frac{1}{2}(\hat y^{(i)}-y^{(i)})^2
$$
为了降低损失,定义平均损失函数为:
$$
L(w,b)=\frac{1}{n}\sum l^{(i)}(w,b)=\frac{1}{n}\sum \frac{1}{2}(w^\mathrm Tx^{(i)}+b-y^{(i)})^2
$$
要求的即是:
$$
w^\ast,b^\ast=\arg \mathop{\min}\limits_{w,b} L(w,b)
$$
求解时利用随机梯度下降法解出 $w,b$ :
$$
w\leftarrow w-\frac{\eta}{|\beta|}\sum\partial_{w}l^{(i)}(w,b)=w-\frac{\eta}{|\beta|}\sum x^{(i)}(w^\mathrm Tx^{(i)}+b-y^{(i)})
$$ { }

$$
b\leftarrow b-\frac{\eta}{|\beta|}\sum\partial_bl^{(i)}(w,b)=w-\frac{\eta}{|\beta|}\sum (w^\mathrm Tx^{(i)}+b-y^{(i)})
$$

$\beta$ 是选择的一个小样本,所有的 $i$ 都是来自 $\beta$ 。

线性回归的实现(TensorFlow的高级API)

  1. 生成、读取数据集;
  2. 定义模型;
  3. 定义损失函数、优化器;
  4. 设置超参数进行训练

生成数据集

1
2
3
4
5
6
7
8
import numpy as np
import tensorflow as tf
from d2l import tensorflow as d2l
###设定标准参数,生成数据集
true_w = tf.constant([2, -3.4])
true_b = 4.2
###分别是x向量与y
features, labels = d2l.synthetic_data(true_w, true_b, 1000)

读取数据集

1
2
3
4
5
6
7
8
9
10
11
12
13
def load_array(data_arrays, batch_size, is_train=True): 
###数据切片,每一条数据仅包含x与对应的y两个元组
dataset = tf.data.Dataset.from_tensor_slices(data_arrays)
###是否打乱
if is_train:
dataset = dataset.shuffle(buffer_size=1000)
###将dataset分成x和y两个元组,但是每个元组有batch个元素,多个元组构成dataset
dataset = dataset.batch(batch_size)
return dataset

###data_iter中每一项包含10条数据
batch_size = 10
data_iter = load_array((features, labels), batch_size)

生成模型及操作

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
###定义net为一个Sequential类的实例
###全连接层在dense定义,只需要一个输出
###初始化权重w为正态分布,偏置b为0
initializer = tf.initializers.RandomNormal(stddev=0.01)
net = tf.keras.Sequential()
net.add(tf.keras.layers.Dense(1,kernel_initializer=initializer))
###此时并没有真正初始化,只有当输入第一组数据时才开始初始化

###定义损失函数平方范数
loss = tf.keras.losses.MeanSquaredError()

###定义优化器为小批量随机梯度下降
trainer = tf.keras.optimizers.SGD(learning_rate=0.03)

###训练
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
###创建梯度带并跟踪其下的运算,一般把需要求梯度的量放在这下面
with tf.GradientTape() as tape:
l = loss(net(X, training=True), y)
###求出l关于w与b分别的梯度,net.trainable_variables包括权重与偏置
grads = tape.gradient(l, net.trainable_variables)
###用优化器更新参数
trainer.apply_gradients(zip(grads, net.trainable_variables))
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l:f}')

###输出
w = net.get_weights()[0]
b = net.get_weights()[1]
print(w,'\n',b)