预测函数:
fθ(x)=θ0+θ1x+θ2x2 f_\theta(x)=\theta_0+\theta_1x+\theta_2x^2 fθ(x)=θ0+θ1x+θ2x2
目标函数(要使它最小):
E(θ)=∑i=1n(y(i)−fθ(x(i)))2 E(\theta)=\sum_{i=1}^{n}{(y^{(i)}-f_\theta(x^{(i)}))^2} E(θ)=i=1n(y(i)fθ(x(i)))2

如何表示预测函数?

预测函数:
fθ(x)=θ0+θ1x+θ2x2 f_\theta(x)=\theta_0+\theta_1x+\theta_2x^2 fθ(x)=θ0+θ1x+θ2x2
这只是一个数据的表达式,当有nnn个训练数据时呢?
θ=[θ0θ1θ2]  x(i)=[1x(i)x(i)2]  X=[x(1)Tx(2)Tx(3)T⋮x(n)T]=[1x(1)x(1)21x(2)x(2)21x(3)x(3)2⋮1x(n)x(n)2] \bm{\theta}= \begin{bmatrix} \begin{matrix} \theta_0\\ \theta_1\\ \theta_2\\ \end{matrix} \end{bmatrix} \space\space \bm{x}^{(i)}= \begin{bmatrix} \begin{matrix} 1\\ x^{(i)}\\ x^{(i)^{2}}\\ \end{matrix} \end{bmatrix} \space\space \bm{X}= \begin{bmatrix} \begin{matrix} x^{(1)^{T}}\\ x^{(2)^{T}}\\ x^{(3)^{T}}\\ \vdots\\ x^{(n)^{T}}\\ \end{matrix} \end{bmatrix}= \begin{bmatrix} \begin{matrix} 1&x^{(1)}&x^{(1)^{2}}\\ 1&x^{(2)}&x^{(2)^{2}}\\ 1&x^{(3)}&x^{(3)^{2}}\\ \vdots\\ 1&x^{(n)}&x^{(n)^{2}}\\ \end{matrix} \end{bmatrix} θ= θ0θ1θ2   x(i)= 1x(i)x(i)2   X= x(1)Tx(2)Tx(3)Tx(n)T = 1111x(1)x(2)x(3)x(n)x(1)2x(2)2x(3)2x(n)2
Xθ=[1x(1)x(1)21x(2)x(2)21x(3)x(3)2⋮1x(n)x(n)2][θ0θ1θ2]=[θ0+θ1x(1)+θ2x(1)2θ0+θ1x(2)+θ2x(2)2θ0+θ1x(3)+θ2x(3)2⋮θ0+θ1x(n)+θ2x(n)2] \bm{X}\bm{\theta}= \begin{bmatrix} \begin{matrix} 1&x^{(1)}&x^{(1)^{2}}\\ 1&x^{(2)}&x^{(2)^{2}}\\ 1&x^{(3)}&x^{(3)^{2}}\\ \vdots\\ 1&x^{(n)}&x^{(n)^{2}}\\ \end{matrix} \end{bmatrix} \begin{bmatrix} \begin{matrix} \theta_0\\ \theta_1\\ \theta_2\\ \end{matrix} \end{bmatrix}= \begin{bmatrix} \begin{matrix} \theta_0+\theta_1x^{(1)}+\theta_2x^{(1)^{2}}\\ \theta_0+\theta_1x^{(2)}+\theta_2x^{(2)^{2}}\\ \theta_0+\theta_1x^{(3)}+\theta_2x^{(3)^{2}}\\ \vdots\\ \theta_0+\theta_1x^{(n)}+\theta_2x^{(n)^{2}}\\ \end{matrix} \end{bmatrix} Xθ= 1111x(1)x(2)x(3)x(n)x(1)2x(2)2x(3)2x(n)2 θ0θ1θ2 = θ0+θ1x(1)+θ2x(1)2θ0+θ1x(2)+θ2x(2)2θ0+θ1x(3)+θ2x(3)2θ0+θ1x(n)+θ2x(n)2
Python代码:

# 构建训练矩阵
mu = train_x.mean()
sigma = train_x.std()

## 为了加快收敛速度进行标准化
def standardize(x):
    return (x - mu) / sigma

train_z = standardize(train_x)

## 先将一维数组垂直叠加(vstack)为3行n列的多维数组再转置为n行3列的多维数组
def to_matrix(x):
    return np.vstack((np.ones(len(x)), x, x ** 2)).T

X = to_matrix(train_z)

# 初始化θ为一维数组
theta = np.random.rand(3)


# 构建预测函数,X的形状为(n×3),θ的形状为(3),即一维数组,系统会自动调整θ的形状为(3×1),相乘后形状为(1×n),最后将f(X)还原成向量,形状为(n,)
def f(X):
    return np.dot(X, theta)

函数说明:

numpy.dot():
对于两个一维的数组,计算的是这两个数组对应下标元素的乘积和(数学上称之为内积);
对于二维数组,计算的是两个数组的矩阵乘积;
对于多维数组,它的通用计算公式如下,即结果数组中的每个元素都是:数组a的最后一维上的所有元素与数组b的倒数第二位上的所有元素的乘积和: dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])。
————————————————————————————————————————
特别地,当多维数组与一维数组相乘时,会自动将一维数组变为矩阵,并自动对矩阵进行转置,然后进行矩阵乘法运算,最后结果还原成向量

np.dot与乘号(*)的区别:

NumPy 中有一个功能用于数组元素间运算,称为广播。通常 NumPy 数
组之间做运算时,数组的形状必须一致,但是在两个数组形状不一致却有可
能调整为一致时,该功能就会先调整再进行运算。文字的说明可能不容易理
解,下面通过示例来演示这个功能。
在这里插入图片描述

如何表示参数更新表达式?

参数更新表达式:
θj:=θj−η∑i=1n(fθ(x(i))−y(i))xj(i) \theta_j:=\theta_j-\eta\sum_{i=1}^{n}{(f_\theta(x^{(i)})-y^{(i)})x_j^{(i)}} θj:=θjηi=1n(fθ(x(i))y(i))xj(i)
不难发现不同的θ\thetaθ有不同的参数表达式。因此:
① 我们要利用循环依次对nnn个训练数据求和;
② 我们要分别对三个θ\thetaθ进行更新。
但是按照上述方式计算量过大,能不能利用矩阵一次性完成上述两个任务呢?答案是可以的。
我们将表达式展开:
j=0j=0j=0时:
θ0:=θ0−η((fθ(x(1))−y(1))x0(1)+(fθ(x(2))−y(2))x0(2)+⋯+(fθ(x(n))−y(n))x0(n) \theta_0:=\theta_0-\eta((f_{\theta}(x^{(1)})-y^{(1)})x_0^{(1)}+(f_{\theta}(x^{(2)})-y^{(2)})x_0^{(2)}+ \dots+(f_{\theta}(x^{(n)})-y^{(n)})x_0^{(n)} θ0:=θ0η((fθ(x(1))y(1))x0(1)+(fθ(x(2))y(2))x0(2)++(fθ(x(n))y(n))x0(n)
不难发现:(fθ(x(i))−y(i))(f_{\theta}(x^{(i)})-y^{(i)})(fθ(x(i))y(i))构成一行,x0(i)x_{0}^{(i)}x0(i)构成一列,两者相乘得到θ0\theta_0θ0对应的部分更新表达式。

j=1j=1j=1时:
θ1:=θ1−η((fθ(x(1))−y(1))x1(1)+(fθ(x(2))−y(2))x1(2)+⋯+(fθ(x(n))−y(n))x1(n) \theta_1:=\theta_1-\eta((f_{\theta}(x^{(1)})-y^{(1)})x_1^{(1)}+(f_{\theta}(x^{(2)})-y^{(2)})x_1^{(2)}+ \dots+(f_{\theta}(x^{(n)})-y^{(n)})x_1^{(n)} θ1:=θ1η((fθ(x(1))y(1))x1(1)+(fθ(x(2))y(2))x1(2)++(fθ(x(n))y(n))x1(n)
不难发现:(fθ(x(i))−y(i))(f_{\theta}(x^{(i)})-y^{(i)})(fθ(x(i))y(i))构成一行,x1(i)x_{1}^{(i)}x1(i)构成一列,两者相乘得到θ1\theta_1θ1对应的部分更新表达式。

j=2j=2j=2时:
θ2:=θ1−η((fθ(x(1))−y(1))x2(1)+(fθ(x(2))−y(2))x2(2)+⋯+(fθ(x(n))−y(n))x2(n) \theta_2:=\theta_1-\eta((f_{\theta}(x^{(1)})-y^{(1)})x_2^{(1)}+(f_{\theta}(x^{(2)})-y^{(2)})x_2^{(2)}+ \dots+(f_{\theta}(x^{(n)})-y^{(n)})x_2^{(n)} θ2:=θ1η((fθ(x(1))y(1))x2(1)+(fθ(x(2))y(2))x2(2)++(fθ(x(n))y(n))x2(n)
不难发现:(fθ(x(i))−y(i))(f_{\theta}(x^{(i)})-y^{(i)})(fθ(x(i))y(i))构成一行,x2(i)x_{2}^{(i)}x2(i)构成一列,两者相乘得到θ1\theta_1θ1对应的部分更新表达式。

因此,令 Y=[y(1)y(2)⋮y(n)]\bm{Y}= \begin{bmatrix} \begin{matrix} y^{(1)}\\ y^{(2)}\\ \vdots\\ y^{(n)}\\ \end{matrix} \end{bmatrix}Y= y(1)y(2)y(n) ,又有X=[1x(1)x(1)21x(2)x(2)21x(3)x(3)2⋮1x(n)x(n)2]\bm{X}= \begin{bmatrix} \begin{matrix} 1&x^{(1)}&x^{(1)^{2}}\\ 1&x^{(2)}&x^{(2)^{2}}\\ 1&x^{(3)}&x^{(3)^{2}}\\ \vdots\\ 1&x^{(n)}&x^{(n)^{2}}\\ \end{matrix} \end{bmatrix}X= 1111x(1)x(2)x(3)x(n)x(1)2x(2)2x(3)2x(n)2 θ=[θ0θ1θ2]\bm{\theta}= \begin{bmatrix} \begin{matrix} \theta_0\\ \theta_1\\ \theta_2\\ \end{matrix} \end{bmatrix}θ= θ0θ1θ2 不难得出:
f=[fθ(x(1))−y(1)fθ(x(2))−y(2)⋮fθ(x(n))−y(n)]=Xθ−Y \bm{f}= \begin{bmatrix} \begin{matrix} f_{\theta}(\bm{x}^{(1)})-y^{(1)}\\ f_{\theta}(\bm{x}^{(2)})-y^{(2)}\\ \vdots\\ f_{\theta}(\bm{x}^{(n)})-y^{(n)}\\ \end{matrix} \end{bmatrix}= \bm{X}\bm{\theta}-\bm{Y} f= fθ(x(1))y(1)fθ(x(2))y(2)fθ(x(n))y(n) =XθY
θ:=XTf \bm{\theta}:=\bm{X}^T\bm{f} θ:=XTf
Python代码:

# 更新参数,f(X)和train_y的形状为(n,),X的形状为(n×3),自动将一维数组(f(X) - train_y)转化为形状为(1×n)的矩阵,与X相乘后形状为(1×3),最后将f(X)还原成向量,形状为(3,)
    theta = theta - ETA * np.dot(f(X) - train_y, X)

评估

均方误差:
1n∑i=1n(y(i)−fθ(x(i))2 \frac{1}{n}\sum_{i=1}^{n}{(y^{(i)}-f_{\bm{\theta}}(\bm{x}^{(i)})^2} n1i=1n(y(i)fθ(x(i))2
Python代码:

# 均方误差,shape[0]读取矩阵第一维度的长度,即数组的行数。
def MSE(x, y):
    return (1 / x.shape[0]) * np.sum((y - f(x)) ** 2)

# 均方误差的历史记录
errors = []

# 重复学习
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]

    # 输出日志
    count += 1
    log = '第{}次:theta0 = {:.3f}, theta1 = {:.3f}, theta2 = {:.3f}, 差值 = {:.4f}'
    print(log.format(count, theta[0], theta[1], theta[2], diff))

# 绘制误差变化图
x=np.arange(len(errors))
plt.plot(x,errors)
plt.show() 

随机梯度下降法的实现

θj:=θj−(fθ(x(k))−y(k))xj(k) \theta_j:=\theta_j-(f_{\theta}(x^{(k)})-y^{(k)})x_j^{(k)} θj:=θj(fθ(x(k))y(k))xj(k)
Python代码:

# 重复学习
errors.append(MSE(X, train_y))
while diff > 1e-2:
    # 为了调整训练数据的顺序,准备随机的序列,输出的是一个序列,假如X.shape[0]=20,那么p是包含0-19的随机序列
    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]

    # 输出日志
    count += 1
    log = '第{}次:theta0 = {:.3f}, theta1 = {:.3f}, theta2 = {:.3f}, 差值 = {:.4f}'
    print(log.format(count, theta[0], theta[1], theta[2], diff))

对 zip 函数的理解:

这里是引用
运行结果:
在这里插入图片描述

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐