1. 问题背景
波士顿房价 数据集包含506条波士顿的城镇信息,每一条城镇信息都包含了14个属性的值,希望从该数据集找到城镇房价中位数 与其它13属性之间存在的关系或规律,使得给出波士顿的一个城镇的前13个属性的值,就能够预测出该城镇的房价中位数。
这是一个典型的可以用线性回归 (linear regression)算法解决的问题。线性回归算法是一种回归算法,属于监督学习算法,它使用的数据集既有特征又有标记,样本标记和预测结果都是连续值。
2. 数据表示
2.1. 特征向量
使用用特征向量表示数据集中的一个样本,一个样本的特征向量包含了该样本所有特征(feature)的值,但不包含需要预测的那一个属性的值:
x ( i ) = ( x 1 ( i ) x 2 ( i ) ⋮ x n ( i ) )
\boldsymbol{{x}^{(i)}} = \begin{pmatrix}
x_{1}^{(i)} \\
x_{2}^{(i)} \\
\vdots \\
x_{n}^{(i)}
\end{pmatrix}
x ( i ) = ⎝ ⎜ ⎜ ⎜ ⎜ ⎛ x 1 ( i ) x 2 ( i ) ⋮ x n ( i ) ⎠ ⎟ ⎟ ⎟ ⎟ ⎞
x ( i ) \boldsymbol{{x}^{(i)}} x ( i ) 是第i i i 个样本(sample)的特征向量。
x j ( i ) x_{j}^{(i)} x j ( i ) 是第i i i 个样本的第j j j 个特征(feature)的值。
n n n 是特征的数量,一个特征向量包含了一个样本的n n n 个特征的值,在波士顿房价数据集中,n = 13 n = 13 n = 1 3 。
粗体的字母表示矩阵或向量,包含多个数值;未加粗的字母表示标量,表示单个数值。
2.2. 特征矩阵
所有样本的特征向量组成了特征矩阵:
X = ( x 1 ( 1 ) x 2 ( 1 ) … x n ( 1 ) x 1 ( 2 ) x 2 ( 2 ) … x 2 ( n ) ⋮ ⋮ ⋱ ⋮ x 1 ( m ) x 2 ( m ) … x n ( m ) ) = ( x ( 1 ) T x ( 2 ) T ⋮ x ( m ) T )
\boldsymbol{X} = \begin{pmatrix}
x_{1}^{(1)} & x_{2}^{(1)} & \ldots & x_{n}^{(1)} \\
x_{1}^{(2)} & x_{2}^{(2)} & \ldots & x_{2}^{(n)} \\
\vdots & \vdots & \ddots & \vdots \\
x_{1}^{(m)} & x_{2}^{(m)} & \ldots & x_{n}^{(m)}
\end{pmatrix} = \begin{pmatrix}
{\boldsymbol{{x}^{(1)}}}^{T} \\
{\boldsymbol{{x}^{(2)}}}^{T} \\
\vdots \\
{\boldsymbol{{x}^{(m)}}}^{T}
\end{pmatrix}
X = ⎝ ⎜ ⎜ ⎜ ⎜ ⎛ x 1 ( 1 ) x 1 ( 2 ) ⋮ x 1 ( m ) x 2 ( 1 ) x 2 ( 2 ) ⋮ x 2 ( m ) … … ⋱ … x n ( 1 ) x 2 ( n ) ⋮ x n ( m ) ⎠ ⎟ ⎟ ⎟ ⎟ ⎞ = ⎝ ⎜ ⎜ ⎜ ⎜ ⎛ x ( 1 ) T x ( 2 ) T ⋮ x ( m ) T ⎠ ⎟ ⎟ ⎟ ⎟ ⎞
X \boldsymbol{X} X 是特征矩阵,每一行的值由一个样本的特征向量的值组成。
m m m 是样本的数量。
n n n 是特征的数量。
x ( i ) T = ( x 1 ( i ) x 2 ( i ) … x n ( i ) ) {\boldsymbol{{x}^{(i)}}}^{T} = \begin{pmatrix} x_{1}^{(i)} & x_{2}^{(i)} & \ldots & x_{n}^{(i)} \end{pmatrix} x ( i ) T = ( x 1 ( i ) x 2 ( i ) … x n ( i ) ) 是第i i i 个样本的特征向量的行向量形式。
向量通常是指列向量,加转置符号T ^{T} T 的向量表示行向量。
2.3. 样本标记
样本标记是需要预测的样本的属性的值:
y ( i )
y^{(i)}
y ( i )
所有样本的标记组成了样本标记的向量:
y = ( y ( 1 ) y ( 2 ) ⋮ y ( m ) )
\boldsymbol{y} = \begin{pmatrix}
y^{(1)} \\
y^{(2)} \\
\vdots \\
y^{(m)}
\end{pmatrix}
y = ⎝ ⎜ ⎜ ⎜ ⎛ y ( 1 ) y ( 2 ) ⋮ y ( m ) ⎠ ⎟ ⎟ ⎟ ⎞
3. 模型
对于一个样本:
y ^ ( i ) = h w , b ( x ( i ) ) = w T x ( i ) + b = w 1 x 1 ( i ) + w 2 x 2 ( i ) + … + w n x n ( i ) + b
\begin{aligned}
\hat{y}^{(i)} = & h_{\boldsymbol{w}, b}(\boldsymbol{x^{(i)}}) \\
= & \boldsymbol{w}^{T} \boldsymbol{x^{(i)}} + b \\
= & w_{1} x_{1}^{(i)} + w_{2} x_{2}^{(i)} + \ldots + w_{n} x_{n}^{(i)} + b
\end{aligned}
y ^ ( i ) = = = h w , b ( x ( i ) ) w T x ( i ) + b w 1 x 1 ( i ) + w 2 x 2 ( i ) + … + w n x n ( i ) + b
对于所有样本:
y ^ = h w , b ( X ) = X w + b
\begin{aligned}
\boldsymbol{\hat{y}} = & h_{\boldsymbol{w}, b}(\boldsymbol{X}) \\
= & \boldsymbol{X} \boldsymbol{w} + b
\end{aligned}
y ^ = = h w , b ( X ) X w + b
y ^ ( i ) \hat{y}^{(i)} y ^ ( i ) 是模型(model)根据第i i i 个样本的特征向量预测出的值。
y ^ \boldsymbol{\hat{y}} y ^ 是模型根据所有样本的特征矩阵预测出的值,包含每一个样本的预测值。
h w , b h_{\boldsymbol{w}, b} h w , b 是模型的数学表示,在上面的公式中相当于一个函数名,w \boldsymbol{{w}} w 和 b b b 相当于函数的常量,x ( i ) \boldsymbol{x^{(i)}} x ( i ) 和X \boldsymbol{X} X 相当于函数的自变量。
w = ( w 0 w 1 … w n ) T \boldsymbol{w}= \begin{pmatrix} w_{0} & w_{1} & \ldots & w_{n} \end{pmatrix}^{T} w = ( w 0 w 1 … w n ) T 是权重(weight),b b b 是偏置项(bias),w \boldsymbol{w} w 和b b b 是需要训练学习的模型参数(parameters)。
从直观上理解,需要预测的值应该与样本的特征有关,给每个特征一个权重,表示每个特征对预测结果的影响程度,偏置项则是考虑特征以外的影响。
利用向量化的方法,可以一次性对所有样本的特征矩阵进行处理。NumPy内部的并行机制能够很高效地执行多维数组的运算,比自己写循环要快很多。
代码实现:
"""
inputs: type=numpy.ndarray, shape=(num_samples, num_features)
weights: type=numpy.ndarray, shape=(num_features, 1)
bias: type=float
outputs: type=numpy.ndarray, shape=(num_samples, 1)
"""
outputs = inputs @ weights + bias
这里没有使用数学符号命名代码中的变量,主要是考虑到可读性和Python编程规范。很多人在实现机器学习算法时使用数学符号命名变量也是可以的,但是在算法比较复杂时,使用数学符号命名变量会导致代码不易理解,同时大写字母开头的变量名违背了Python的变量命名规范。
4. 训练
4.1. 损失函数和代价函数
损失函数(loss function)的值是模型在一个单独的样本上的误差。
线性回归常用的损失函数是平方误差函数(squared error function):
L ( y ^ ( i ) , y ( i ) ) = 1 2 ( y ^ ( i ) − y ( i ) ) 2
L(\hat{y}^{(i)}, y^{(i)}) = \frac{1}{2} (\hat{y}^{(i)} - y^{(i)})^{2}
L ( y ^ ( i ) , y ( i ) ) = 2 1 ( y ^ ( i ) − y ( i ) ) 2
代价函数(cost function)的值是模型在所有样本上的误差的均值,代价函数以w \boldsymbol{w} w 和b b b 作为自变量。
线性回归的代价函数是所有样本的平方误差的均值:
J ( w , b ) = 1 2 m ∑ i = 1 m ( y ^ ( i ) − y ( i ) ) 2
J(\boldsymbol{w}, b) = \frac{1}{2m} \sum_{i = 1}^{m} (\hat{y}^{(i)} - y^{(i)})^{2}
J ( w , b ) = 2 m 1 i = 1 ∑ m ( y ^ ( i ) − y ( i ) ) 2
很多人甚至是学术界中,通常不使用损失函数,而是将代价函数称为损失函数。两者的概念通常不必过分细究。
代码实现(2种方式):
"""
outputs: type=numpy.ndarray, shape=(num_samples, 1)
labels: type=numpy.ndarray, shape=(num_samples, 1)
num_samples: type=int
loss: type=float
diff: type=numpy.ndarray, shape=(num_samples, 1)
"""
loss = 0.5 / num_samples * np. sum ( ( outputs - labels) ** 2 )
diff = outputs - labels
loss = 0.5 / num_samples * diff. T @ diff
Python中通常使用小写字母和单词间加下划线的形式命名变量和函数,可以使用一些约定俗成的缩写命名变量和函数,不违背Python编程规范。与许多编程语言推荐使用的驼峰命名法不同。不管采用哪种命名方式,只要没有语法错误就可以,但建议遵循每种编程语言各自的编程规范,每种语言的主流IDE基本都支持编程规范检查。
4.2. 优化目标
代价函数的值越小,代表模型的预测越接近于实际情况。为了使模型表现更好,需要不断降低代价函数的值,优化目标(optimization object)即为求解模型的常量w \boldsymbol{w} w 和b b b 使得J ( w , b ) J(\boldsymbol{w}, b) J ( w , b ) 最小化。
4.3. 梯度下降
通常使用梯度下降(gradient descent)求解w \boldsymbol{w} w 和b b b :
w : = w − α ∂ ∂ w J ( w , b ) b : = b − α ∂ ∂ b J ( w , b )
\begin{aligned}
\boldsymbol{w} & := \boldsymbol{w} - \alpha \frac{\partial}{\partial \boldsymbol{w}} J(\boldsymbol{w}, b) \\
b & := b - \alpha \frac{\partial}{\partial b} J(\boldsymbol{w}, b)
\end{aligned}
w b : = w − α ∂ w ∂ J ( w , b ) : = b − α ∂ b ∂ J ( w , b )
α \alpha α 是学习率(learning rate),表示参数沿梯度下降的幅度大小。
∂ ∂ w J ( w , b ) \frac{\partial}{\partial \boldsymbol{w}} J(\boldsymbol{w}, b) ∂ w ∂ J ( w , b ) 是J J J 在w \boldsymbol{w} w 上的偏导:∂ ∂ w J ( w , b ) = 1 m X T ( y ^ − y )
\frac{\partial}{\partial \boldsymbol{w}} J(\boldsymbol{w}, b) = \frac{1}{m} \boldsymbol{X}^{T} (\boldsymbol{\hat{y}} - \boldsymbol{y})
∂ w ∂ J ( w , b ) = m 1 X T ( y ^ − y )
∂ ∂ b J ( w , b ) \frac{\partial}{\partial b} J(\boldsymbol{w}, b) ∂ b ∂ J ( w , b ) 是J J J 在b b b 上的偏导:∂ ∂ b J ( w , b ) = 1 m ∑ i = 1 m ( y ^ − y )
\frac{\partial}{\partial b} J(\boldsymbol{w}, b) = \frac{1}{m} \sum_{i = 1}^{m} (\boldsymbol{\hat{y}} - \boldsymbol{y})
∂ b ∂ J ( w , b ) = m 1 i = 1 ∑ m ( y ^ − y )
线性回归中使用梯度下降的优点:在特征数n很大时效果较好。
线性回归中使用梯度下降的缺点:需要选取学习率;需要多次迭代。
求偏导的过程:
∂ ∂ w 1 J ( w 1 , b ) = ∂ ∂ w 1 ( 1 2 m ∑ i = 1 m ( y ^ ( i ) − y ( i ) ) 2 ) = ∂ ∂ w 1 ( 1 2 m [ ( y ^ ( 1 ) − y ( 1 ) ) 2 + . . . + ( y ^ ( m ) − y ( m ) ) 2 ] ) = 1 m ( y ^ ( 1 ) − y ( 1 ) ) ∂ ∂ w 1 ( y ^ ( 1 ) − y ( 1 ) ) + . . . + 1 m ( y ^ ( m ) − y ( m ) ) ∂ ∂ w 1 ( y ^ ( m ) − y ( m ) ) = 1 m ( y ^ ( 1 ) − y ( 1 ) ) ∂ ∂ w 1 ( w 1 x 1 ( 1 ) … + w n x n ( 1 ) + b − y ( 1 ) ) + . . . + 1 m ( y ^ ( m ) − y ( m ) ) ∂ ∂ w 1 ( w 1 x 1 ( m ) … + w n x n ( m ) + b − y ( m ) ) = 1 m [ ( y ^ ( 1 ) − y ( 1 ) ) x 1 ( 1 ) + . . . + ( y ^ ( m ) − y ( m ) ) x 1 ( m ) ]
\begin{aligned}
\frac{\partial}{\partial w_{1}} J (w_{1}, b) = & \frac{\partial}{\partial w_{1}} (\frac{1}{2m} \sum_{i = 1}^{m} (\hat{y}^{(i)} - y^{(i)})^{2}) \\
= & \frac{\partial}{\partial w_{1}} (\frac{1}{2m} [(\hat{y}^{(1)}- y^{(1)})^{2} + ... + (\hat{y}^{(m)} - y^{(m)})^{2}]) \\
= & \frac{1}{m} (\hat{y}^{(1)} - y^{(1)}) \frac{\partial}{\partial w_{1}} (\hat{y}^{(1)} - y^{(1)}) + ... \\
& + \frac{1}{m} (\hat{y}^{(m)} - y^{(m)}) \frac{\partial}{\partial w_{1}} (\hat{y}^{(m)} - y^{(m)}) \\
= & \frac{1}{m} (\hat{y}^{(1)} - y^{(1)}) \frac{\partial}{\partial w_{1}} (w_{1} x_{1}^{(1)} \ldots + w_{n} x_{n}^{(1)} + b - y^{(1)}) + ... \\
& + \frac{1}{m} (\hat{y}^{(m)} - y^{(m)}) \frac{\partial}{\partial w_{1}} (w_{1} x_{1}^{(m)} \ldots + w_{n} x_{n}^{(m)} + b - y^{(m)}) \\
= & \frac{1}{m} [(\hat{y}^{(1)} - y^{(1)}) x_{1}^{(1)} + ... + (\hat{y}^{(m)} - y^{(m)}) x_{1}^{(m)}]
\end{aligned}
∂ w 1 ∂ J ( w 1 , b ) = = = = = ∂ w 1 ∂ ( 2 m 1 i = 1 ∑ m ( y ^ ( i ) − y ( i ) ) 2 ) ∂ w 1 ∂ ( 2 m 1 [ ( y ^ ( 1 ) − y ( 1 ) ) 2 + . . . + ( y ^ ( m ) − y ( m ) ) 2 ] ) m 1 ( y ^ ( 1 ) − y ( 1 ) ) ∂ w 1 ∂ ( y ^ ( 1 ) − y ( 1 ) ) + . . . + m 1 ( y ^ ( m ) − y ( m ) ) ∂ w 1 ∂ ( y ^ ( m ) − y ( m ) ) m 1 ( y ^ ( 1 ) − y ( 1 ) ) ∂ w 1 ∂ ( w 1 x 1 ( 1 ) … + w n x n ( 1 ) + b − y ( 1 ) ) + . . . + m 1 ( y ^ ( m ) − y ( m ) ) ∂ w 1 ∂ ( w 1 x 1 ( m ) … + w n x n ( m ) + b − y ( m ) ) m 1 [ ( y ^ ( 1 ) − y ( 1 ) ) x 1 ( 1 ) + . . . + ( y ^ ( m ) − y ( m ) ) x 1 ( m ) ]
∂ ∂ w J ( w , b ) = ( ∂ ∂ w 1 J ( w 1 , b ) ∂ ∂ w 2 J ( w 2 , b ) ⋮ ∂ ∂ w n J ( w n , b ) ) = 1 m ( ( y ^ ( 1 ) − y ( 1 ) ) x 1 ( 1 ) + . . . + ( y ^ ( m ) − y ( m ) ) x 1 ( m ) ( y ^ ( 1 ) − y ( 1 ) ) x 2 ( 1 ) + . . . + ( y ^ ( m ) − y ( m ) ) x 2 ( m ) ⋮ ( y ^ ( 1 ) − y ( 1 ) ) x n ( 1 ) + . . . + ( y ^ ( m ) − y ( m ) ) x n ( m ) ) = 1 m X T ( y ^ − y )
\begin{aligned}
\frac{\partial}{\partial \boldsymbol{w}} J(\boldsymbol{w}, b) = & \begin{pmatrix}
\frac{\partial}{\partial w_{1}} J (w_{1}, b) \\
\frac{\partial}{\partial w_{2}} J (w_{2}, b) \\
\vdots \\
\frac{\partial}{\partial w_{n}} J (w_{n}, b)
\end{pmatrix} \\
= &\frac{1}{m} \begin{pmatrix}
(\hat{y}^{(1)} - y^{(1)}) x_{1}^{(1)} + ... + (\hat{y}^{(m)} - y^{(m)}) x_{1}^{(m)} \\
(\hat{y}^{(1)} - y^{(1)}) x_{2}^{(1)} + ... + (\hat{y}^{(m)} - y^{(m)}) x_{2}^{(m)} \\
\vdots \\
(\hat{y}^{(1)} - y^{(1)}) x_{n}^{(1)} + ... + (\hat{y}^{(m)} - y^{(m)}) x_{n}^{(m)}
\end{pmatrix} \\
= & \frac{1}{m} \boldsymbol{X}^{T} (\boldsymbol{\hat{y}} - \boldsymbol{y})
\end{aligned}
∂ w ∂ J ( w , b ) = = = ⎝ ⎜ ⎜ ⎜ ⎛ ∂ w 1 ∂ J ( w 1 , b ) ∂ w 2 ∂ J ( w 2 , b ) ⋮ ∂ w n ∂ J ( w n , b ) ⎠ ⎟ ⎟ ⎟ ⎞ m 1 ⎝ ⎜ ⎜ ⎜ ⎜ ⎛ ( y ^ ( 1 ) − y ( 1 ) ) x 1 ( 1 ) + . . . + ( y ^ ( m ) − y ( m ) ) x 1 ( m ) ( y ^ ( 1 ) − y ( 1 ) ) x 2 ( 1 ) + . . . + ( y ^ ( m ) − y ( m ) ) x 2 ( m ) ⋮ ( y ^ ( 1 ) − y ( 1 ) ) x n ( 1 ) + . . . + ( y ^ ( m ) − y ( m ) ) x n ( m ) ⎠ ⎟ ⎟ ⎟ ⎟ ⎞ m 1 X T ( y ^ − y )
上式最后一步可以反推回去验证。
∂ ∂ b J ( w 1 , b ) = ∂ ∂ b ( 1 2 m ∑ i = 1 m ( y ^ ( i ) − y ( i ) ) 2 ) = ∂ ∂ b ( 1 2 m [ ( y ^ ( 1 ) − y ( 1 ) ) 2 + . . . + ( y ^ ( m ) − y ( m ) ) 2 ] ) = 1 m ( y ^ ( 1 ) − y ( 1 ) ) ∂ ∂ b ( y ^ ( 1 ) − y ( 1 ) ) + . . . + 1 m ( y ^ ( m ) − y ( m ) ) ∂ ∂ b ( y ^ ( m ) − y ( m ) ) = 1 m ( y ^ ( 1 ) − y ( 1 ) ) ∂ ∂ b ( w 1 x 1 ( 1 ) … + w n x n ( 1 ) + b − y ( 1 ) ) + . . . + 1 m ( y ^ ( m ) − y ( m ) ) ∂ ∂ b ( w 1 x 1 ( m ) … + w n x n ( m ) + b − y ( m ) ) = 1 m [ ( y ^ ( 1 ) − y ( 1 ) ) + . . . + ( y ^ ( m ) − y ( m ) ) ] = 1 m ∑ i = 1 m ( y ^ − y )
\begin{aligned}
\frac{\partial}{\partial b} J (w_{1}, b) = & \frac{\partial}{\partial b} (\frac{1}{2m} \sum_{i = 1}^{m} (\hat{y}^{(i)} - y^{(i)})^{2}) \\
= & \frac{\partial}{\partial b} (\frac{1}{2m} [(\hat{y}^{(1)}- y^{(1)})^{2} + ... + (\hat{y}^{(m)} - y^{(m)})^{2}]) \\
= & \frac{1}{m} (\hat{y}^{(1)} - y^{(1)}) \frac{\partial}{\partial b} (\hat{y}^{(1)} - y^{(1)}) + ... + \frac{1}{m} (\hat{y}^{(m)} - y^{(m)}) \frac{\partial}{\partial b} (\hat{y}^{(m)} - y^{(m)}) \\
= & \frac{1}{m} (\hat{y}^{(1)} - y^{(1)}) \frac{\partial}{\partial b} (w_{1} x_{1}^{(1)} \ldots + w_{n} x_{n}^{(1)} + b - y^{(1)}) + ... \\
& + \frac{1}{m} (\hat{y}^{(m)} - y^{(m)}) \frac{\partial}{\partial b} (w_{1} x_{1}^{(m)} \ldots + w_{n} x_{n}^{(m)} + b - y^{(m)}) \\
= & \frac{1}{m} [(\hat{y}^{(1)} - y^{(1)}) + ... + (\hat{y}^{(m)} - y^{(m)})] \\
= & \frac{1}{m} \sum_{i = 1}^{m} (\boldsymbol{\hat{y}} - \boldsymbol{y})
\end{aligned}
∂ b ∂ J ( w 1 , b ) = = = = = = ∂ b ∂ ( 2 m 1 i = 1 ∑ m ( y ^ ( i ) − y ( i ) ) 2 ) ∂ b ∂ ( 2 m 1 [ ( y ^ ( 1 ) − y ( 1 ) ) 2 + . . . + ( y ^ ( m ) − y ( m ) ) 2 ] ) m 1 ( y ^ ( 1 ) − y ( 1 ) ) ∂ b ∂ ( y ^ ( 1 ) − y ( 1 ) ) + . . . + m 1 ( y ^ ( m ) − y ( m ) ) ∂ b ∂ ( y ^ ( m ) − y ( m ) ) m 1 ( y ^ ( 1 ) − y ( 1 ) ) ∂ b ∂ ( w 1 x 1 ( 1 ) … + w n x n ( 1 ) + b − y ( 1 ) ) + . . . + m 1 ( y ^ ( m ) − y ( m ) ) ∂ b ∂ ( w 1 x 1 ( m ) … + w n x n ( m ) + b − y ( m ) ) m 1 [ ( y ^ ( 1 ) − y ( 1 ) ) + . . . + ( y ^ ( m ) − y ( m ) ) ] m 1 i = 1 ∑ m ( y ^ − y )
代码实现:
"""
outputs: type=numpy.ndarray, shape=(num_samples, 1)
labels: type=numpy.ndarray, shape=(num_samples, 1)
inputs: type=numpy.ndarray, shape=(num_samples, num_features)
num_samples: type=int
learning_rate: type=float
weights: type=numpy.ndarray, shape=(num_features, 1)
bias: type=float
"""
weights -= learning_rate / num_samples * inputs. T @ ( outputs - labels)
bias -= learning_rate / num_samples * np. sum ( outputs - labels)
5. 正规方程
线性回归模型的模型参数还可以使用正规方程(regulation equation,也称闭式解(closed-form solution))求解。
对代价函数求模型参数的导数,令导数为零,解出的模型参数为最优解:
w ^ = ( X T X ) − 1 X T y
\boldsymbol{\hat{w}} = (\boldsymbol{X}^{T} \boldsymbol{X})^{-1} \boldsymbol{X}^{T} \boldsymbol{y}
w ^ = ( X T X ) − 1 X T y
w ^ = ( w ; b ) \boldsymbol{\hat{w}} = (\boldsymbol{w}; b) w ^ = ( w ; b ) 是w \boldsymbol{w} w 和b b b 的拼接。
X \boldsymbol{X} X 不同于之前公式中的X \boldsymbol{X} X 。为了方便运算,在最右边多加一列1 1 1 ,此时:X = ( x 1 ( 1 ) x 2 ( 1 ) … x n ( 1 ) 1 x 1 ( 2 ) x 2 ( 2 ) … x 2 ( n ) 1 ⋮ ⋮ ⋱ ⋮ 1 x 1 ( m ) x 2 ( m ) … x n ( m ) 1 )
\boldsymbol{X} = \begin{pmatrix}
x_{1}^{(1)} & x_{2}^{(1)} & \ldots & x_{n}^{(1)} & 1 \\
x_{1}^{(2)} & x_{2}^{(2)} & \ldots & x_{2}^{(n)} & 1 \\
\vdots & \vdots & \ddots & \vdots & 1 \\
x_{1}^{(m)} & x_{2}^{(m)} & \ldots & x_{n}^{(m)} & 1
\end{pmatrix}
X = ⎝ ⎜ ⎜ ⎜ ⎜ ⎛ x 1 ( 1 ) x 1 ( 2 ) ⋮ x 1 ( m ) x 2 ( 1 ) x 2 ( 2 ) ⋮ x 2 ( m ) … … ⋱ … x n ( 1 ) x 2 ( n ) ⋮ x n ( m ) 1 1 1 1 ⎠ ⎟ ⎟ ⎟ ⎟ ⎞
线性回归的代价函数,即平方误差函数,总是一个凸函数,只有一个全局最优解。
使用正规方程不需要使用特征缩放。
线性回归中使用正规方程的优点:不需要选取学习率;不需要多次迭代的训练。
线性回归中使用正规方程的缺点: 求矩阵的逆时间复杂度为O ( n 3 ) O(n^{3}) O ( n 3 ) ,当特征数n很大时非常耗时;有些矩阵不可逆。
如果矩阵X \boldsymbol{X} X 中有两个特征的值可以通过一个线性方程联系起来,那么X T X \boldsymbol{X}^{T} \boldsymbol{X} X T X 不可逆。解决方法是删除冗余的特征。
如果样本数m m m 小于特征数n n n ,有时候会使X T X \boldsymbol{X}^{T} \boldsymbol{X} X T X 不可逆。解决方法是删除一些特征,或是使用正则化。
求闭式解的过程:
J ( w ^ ) = 1 2 m ∑ i = 1 m ( y ^ ( i ) − y ( i ) ) 2 = 1 2 m ( y ^ − y ) T ( y ^ − y ) = 1 2 m ( X w ^ − y ) T ( X w ^ − y )
\begin{aligned}
J(\boldsymbol{\hat{w}}) = & \frac{1}{2m} \sum_{i = 1}^{m} (\hat{y}^{(i)} - y^{(i)})^{2} \\
= & \frac{1}{2m} (\boldsymbol{\hat{y}} - \boldsymbol{y})^{T} (\boldsymbol{\hat{y}} - \boldsymbol{y}) \\
= & \frac{1}{2m} (\boldsymbol{X} \boldsymbol{\hat{w}} - \boldsymbol{y})^{T} (\boldsymbol{X} \boldsymbol{\hat{w}} - \boldsymbol{y})
\end{aligned}
J ( w ^ ) = = = 2 m 1 i = 1 ∑ m ( y ^ ( i ) − y ( i ) ) 2 2 m 1 ( y ^ − y ) T ( y ^ − y ) 2 m 1 ( X w ^ − y ) T ( X w ^ − y )
J J J 对w ^ \boldsymbol{\hat{w}} w ^ 求偏导:
$$
\begin{aligned}
\frac{\partial}{\partial \boldsymbol{\hat{w}}} J(\boldsymbol{\hat{w}}) = \frac{1}{m} \boldsymbol{X}^{T} (\boldsymbol{X} \boldsymbol{\hat{w}} - \boldsymbol{y})
\end{aligned}
$$
令∂ ∂ w ^ J ( w ^ ) = 0 \frac{\partial}{\partial \boldsymbol{\hat{w}}} J(\boldsymbol{\hat{w}}) = 0 ∂ w ^ ∂ J ( w ^ ) = 0 解得w ^ \boldsymbol{\hat{w}} w ^ :
X T ( X w ^ − y ) = 0 X T X w ^ = X T y w ^ = ( X T X ) − 1 X T y
\begin{aligned}
\boldsymbol{X}^{T} (\boldsymbol{X} \boldsymbol{\hat{w}} - \boldsymbol{y}) = & 0 \\
\boldsymbol{X}^{T} \boldsymbol{X} \boldsymbol{\hat{w}} = & \boldsymbol{X}^{T} \boldsymbol{y} \\
\boldsymbol{\hat{w}} = & (\boldsymbol{X}^{T} \boldsymbol{X})^{-1} \boldsymbol{X}^{T} \boldsymbol{y}
\end{aligned}
X T ( X w ^ − y ) = X T X w ^ = w ^ = 0 X T y ( X T X ) − 1 X T y
代码实现:
"""
labels: type=numpy.ndarray, shape=(num_samples, 1)
inputs: type=numpy.ndarray, shape=(num_samples, num_features)
inputs(new): type=numpy.ndarray, shape=(num_samples, num_features + 1)
inputs_t: type=numpy.ndarray, shape=(num_features + 1, num_samples)
params: type=numpy.ndarray, shape=(num_features + 1, 1)
"""
inputs = np. concatenate( ( inputs, np. ones( ( inputs. shape[ 0 ] , 1 ) ) ) , axis= 1 )
inputs_t = inputs. T
params = np. linalg. inv( inputs_t @ inputs) @ inputs_t @ labels
6. 波士顿房价求解
使用线性回归对其波士顿房价问题求解。代码可以从我的GitHub 克隆或下载,提供了.py
和.ipynb
两个版本。
在终端或控制台运行时,先运行命令export PYTHONPATH=xxx/machine-learning/
将项目目录添加到PYTHONPATH
,修改xxx
为项目的目录。
training: 100%|███████████████████████| 100/100 [00:00<00:00, 18099.18it/s]
loss on training set after training 100 epochs: 34.51055400910424
loss on test set after training 100 epochs: 35.1717284870999
loss on training set by regulation equation: 9.979109907119023
loss on test set by regulation equation: 13.597982883439103
收敛曲线图:
训练结果:
正规方程的结果:
目前来看,使用正规方程的结果要好一些,但梯度下降的方法还可以改善。
7. 参考
sklearn波士顿房价数据集
网易云课堂-吴恩达机器学习视频
机器学习/周志华著. --北京: 清华大学出版社,2016(2016.2重印)
本文内容纯属个人学习过程中的笔记和思考,不作为详细的教程。见解尚浅,承蒙斧正。
Like a Swift, like a Python.
——落空开关,a fallthrough switch