回归
作为一次函数实现
1 2
| import numpy as np import matplotlib.pyplot as plt
|
读入训练数据
1 2 3
| train = np.loadtxt('click.csv', delimiter=',', skiprows=1) train_x = train[:,0] train_y = train[:,1]
|
画图展示
1
| plt.plot(train_x, train_y, 'o')
|
[<matplotlib.lines.Line2D at 0x7fc95f6b1410>]

初始化函数
fθ(x)=θ0+θ1x
E(θ)=21i=1∑n(y(i)−fθ(x(i)))2
编写 fθ(x)
1 2 3 4
| theta0 = np.random.rand() theta1 = np.random.rand() def f(x): return theta0 + theta1 * x
|
编写 E(θ)
1 2
| def E(x, y): return 0.5 * np.sum((y - f(x)) ** 2)
|
z-score规范化 (标准化)
把训练数据变成平均值为0、方差为 1 的数据。这项操作有利于加快参数的收敛。
z(i)=σx(i)−μ
其中 μ 是训练数据的平均值,σ 是标准差(方差的算数平方根)。
1 2 3 4 5 6 7 8
| mu = train_x.mean() sigma = train_x.std()
def standarize(x): return (x - mu) / sigma
train_z = standarize(train_x)
|
展示标准化之后的数据:
1 2
| plt.plot(train_z, train_y, 'o') plt.show()
|

参数更新
θ0:=θ0−ηi=1∑n(fθ(x(i))−y(i))
θ1:=θ1−ηi=1∑n(fθ(x(i))−y(i))x(i)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| theta0 = np.random.rand() theta1 = np.random.rand()
ETA = 1e-3 diff = 1 count = 0 error = E(train_z, train_y) while diff >= 1e-2: tmp0 = theta0 - ETA * np.sum((f(train_z) - train_y)) tmp1 = theta1 - ETA * np.sum((f(train_z) - train_y) * train_z) theta0 = tmp0 theta1 = tmp1 current_error = E(train_z, train_y) diff = error - current_error error = current_error
count += 1 log = ' 第 {} 次 : theta0 = {:.3f}, theta1 = {:.3f}, 差值 = {:.4f}' print(log.format(count, theta0, theta1, diff))
|
第 1 次 : theta0 = 8.958, theta1 = 2.110, 差值 = 76243.2366
第 2 次 : theta0 = 17.362, theta1 = 3.938, 差值 = 73224.0044
...
第 393 次 : theta0 = 428.997, theta1 = 93.446, 差值 = 0.0101
第 394 次 : theta0 = 429.000, theta1 = 93.446, 差值 = 0.0097
1 2 3 4
| x = np.linspace(-3, 3, 100) plt.plot(train_z, train_y, 'o') plt.plot(x, f(x)) plt.show()
|

多项式回归
一般化 fθ(x)
fθ(x)=θ0+θ1x+θ2x2
将参数和训练数据都作为向量来处理,可以使计算变得更简单。
由于训练数据有很多,所以将每一行数据都视作一个训练数据,以矩阵的形式来处理。
X=⎣⎢⎢⎢⎢⎢⎢⎡x(1)Tx(2)Tx(3)T…x(n)T⎦⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎡111…1x(1)x(2)x(3)x(n)x(1)2x(2)2x(3)2x(n)2⎦⎥⎥⎥⎥⎥⎥⎤
于是我们只用求出矩阵与参数向量 θ 的乘积。
fθ(x)=Xθ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| theta = np.random.rand(3)
def to_matrix(x): ''' np.ones(x.shape[0]) 创建一个长度为 x 的数组, 元素全部为1, 表示常数项。 np.vstack(...) 将上述三个数组垂直堆叠, 完成这一步后矩阵变为: [[1,1,...,1], [x1,x2,...,xn], [x1^2,x2^2,...,xn^2]] 后缀 .T 表示转置 ''' return np.vstack([np.ones(x.shape[0]), x, x**2]).T
def f(x): return np.dot(x, theta)
|
一般化参数更新
已推出:
θj:=θj−ηi=1∑n(fθ(x(i))−y(i))xj(i)
把表达式中 fθ(x(i))−y(i) 和 xj(i) 的部分分别当作向量来处理。
通过向量相乘可直接表示:
θj:=θj−fTX
为方便理解向量积有下例:
a=⎣⎢⎡252⎦⎥⎤,b=⎣⎢⎡123⎦⎥⎤ 则 aTb=[252]⎣⎢⎡123⎦⎥⎤=[2×1+5×2+2×3]=[18]
1 2 3 4 5 6 7 8
| diff = 1 X = to_matrix(train_z) error = E(X, train_y) while diff >= 1e-2: theta = theta - ETA * np.dot(f(X) - train_y, X) current_error = E(X, train_y) diff = error - current_error error = current_error
|
展示
1 2 3 4
| x = np.linspace(-3, 3, 100) plt.plot(train_z, train_y, 'o') plt.plot(x, f(to_matrix(x))) plt.show()
|

误差变化可视化
均方误差 MSE=n1i=1∑n(y(i)−fθ(x(i)))2
1 2
| def MSE(x, y): return (1 / x.shape[0]) * np.sum((y - f(x)) ** 2)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| theta = np.random.rand(3) errors = [] diff = 1
errors.append(MSE(X, train_y)) while diff > 1e-2: theta = theta - ETA * np.dot(f(X) - train_y, X) errors.append(MSE(X, train_y)) diff = errors[-2]- errors[-1]
x = np.arange(len(errors))
plt.plot(x, errors)
|
[<matplotlib.lines.Line2D at 0x7fc95f411c50>]

随机梯度下降法
θj:=θj−ηi=1∑n(fθ(x(k))−y(i))xj(k)
1 2
| p = np.random.permutation(X.shape[0]) print(p)
|
[10 16 2 18 0 6 3 11 7 5 4 9 1 15 13 12 17 8 14 19]
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
| theta = np.random.rand(3) errors = [] diff = 1 errors.append(MSE(X, train_y)) ETA = 0.05
while diff > 1e-3: p = np.random.permutation(X.shape[0]) for x, y in zip(X[p,:], train_y[p]): theta = theta - ETA * (f(x) - y) * x errors.append(MSE(X, train_y)) diff = errors[-2] - errors[-1]
x = np.linspace(-3, 3, 100) plt.plot(train_z, train_y, "o") plt.plot(x, f(to_matrix(x))) plt.show()
x = np.arange(len(errors))
plt.plot(x, errors)
|

[<matplotlib.lines.Line2D at 0x7fc95e98c090>]
