机器学习|吴恩达机器学习之线性回归

对《机器学习》这门课程的回顾,系列文章的目的是希望能够把原理代码实现统一起来,增进理解,所以对一些我认为简单的知识,可能会一笔带过。

前言

这里对第一周的内容做一些简单的回顾:

线性回归-单变量(one variable)

基本内容

假设函数

(1)公式:$h_{\theta}(x)=\theta_{0}+\theta_{1}x$

(2)原理:输入一个单变量x,输出预测值

$\theta_{0}$又叫偏置项,也就是截距。如果没有偏置的话,直线必须经过原点,这就导致很多情况下根本没法拟合。

很显然,我们的目标就是要确定$\theta_{0}$和$\theta_{1}$,但是如何确定呢?

关键在于衡量$h_{\theta}(x)$和$y$之间的差别。

代价函数

(1)公式:$J(\theta_{0},\theta_{1})=\frac{1}{2m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})^{2}$,又称“平方误差函数”,这里的1/2是方便后面求导时约掉平方。计算出参数$\theta_{0}$和$\theta_{1}$需要用迭代的方法,而每次迭代都会代入所有的x和y,因此用向量化的方式来代替循环。

(2)原理:最小二乘法判断误差。

由于代价函数是衡量$h_{\theta}(x)$和$y$之间的误差,因此我们的目标就是要找到能使代价函数值最小的$\theta_{0}$和$\theta_{1}$。

梯度下降法

在线性回归问题中,代价函数的图像是凸函数(convex-function),如下图。只要你学过高中数学,你就应该明白黄点处的代价值是最小的(极小值点),也就是说,在黄点处需要满足$\frac{\partial J(\theta_{0}, \theta_{1})}{\partial \theta_{0} }=0$和$\frac{\partial J(\theta_{0}, \theta_{1})}{\partial \theta_{1} }=0$。

直观来说,就是如何解决从初始值“走”到黄点处。

EqUnij.md.png

(1)公式:

一项项来看:

  • 导数项$\frac{1}{m} \sum_{i=1}^{m}\left(h_{\theta}\left(x^{(i)}\right)-y^{(i)}\right) \cdot x_{j}^{(i)}$

    当时我有一个理解不了的地方:

    结合图像来看,我们Random initial value的点在左侧,导数小于0,那么似乎公式中就变成减去一个负值

    其实这么理解是错误的,梯度看起来像是导数在高维的推广,但二者应该有本质上的区别。梯度是矢量而某点的导数是常量,梯度指向了数值最大增大方向,而导数并不指向方向,它仅仅表示了切线的斜率。

    实际应用中一般$\theta_{j},j>=2$,我们不会单独对$\theta_{0}$和$\theta_{1}$求值,把他们向量化是更明智的选择,

    $\left( \begin{array}{c}{\frac{\partial}{\partial \theta_{0}} \operatorname{J}(\theta)} \\ {\frac{\partial}{\partial \theta_{1}} \operatorname{J}(\theta)} \\ {\vdots} \\ {\frac{\partial}{\partial \theta_{n}} \operatorname{J}(\theta)}\end{array}\right)=\frac{1}{m} \mathbf{X}^{T} \cdot(\mathbf{X} \cdot \theta-\mathbf{y})$

    这也就保证了这一项的值必然是个矢量,换句话说,就是求得的一定是梯度。而取负号,意思就是取和数值最大增大方向相反。

  • $\alpha$

    学习速率,直观叫法“步长”。表示的是你每次迭代时移动的位置大小。如果过大,可能会导致函数越过最优点发散;如果过小,将会导致时间开销变大。

最后重复直到收敛。

代码

在实际代码中,数据都是用矩阵的方式来表示。

1.导入几个常用库

1
2
3
4
5
6
import numpy as np
import pandas as pd

import matplotlib.pyplot as plt #画图函数
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.colors import LogNorm

数据处理

1.导入csv文件,用DataFrame结构存储。

(1)关于csv文件:简单来说就是纯文本,以行为一条记录,每条记录被分隔符分隔。CSV文件

(2)pandas中的DataFrame官方手册

(3)关于.describe()返回值中的分位数:浅谈分为数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
'''导入数据'''
path = 'Data\ex1data1.txt'
'''
参数:
path:路径
header:列名,等于None,是因为接下去列名会显示传递
names:需要传递的列名
返回值:
DataFrame形式数据结构
'''
data = pd.read_csv(
path, header=None,
names=['Population',
'Profit'])

# 导入数据以后可视化观察一下
data.head() # 查看记录,默认返回前5条

# 查看数据集的描述信息
# count:样本个数 mean:均值 std:标准差 min:最小值 25%:四分之一位数 50%:中位数 75%:四分之# 三位数 max:最大值
data.describe()

2.对得到的数据进行加工,方便计算

1
2
3
4
5
6
7
8
9
10
'''
从样本集中分离出X和y
'''

data.insert(0, 'one', 1)# 插入第1列:X0=1

cols = data.shape[1] # .shape返回一个元组,[0]为行数,[1]为列数
# 提取X,y的值
X = data.iloc[:, 0:cols - 1]
y = data.iloc[:, cols - 1:cols]

初始化数据矩阵,这里转化成了np.matrix,但建议使用np.ndarray

1
2
3
4
5
6
7
8
9
10
11
'''
数据处理
'''
# 将dataframe结构转化成np的matrix
# 当Theta取0时计算平均误差
X = np.matrix(X.values)
y = np.matrix(y.values)
theta = np.matrix([0, 0])
# 初始化学习速率和迭代次数
alpha = 0.01
epoch = 1000

计算函数

1.代价函数

公式:$J(\theta_{0},\theta_{1})=\frac{1}{2m}\sum_{i=1}^{m}(h_{\theta}(x_{i})-y_{i})^{2}$,其中主要矩阵化:假设函数$h_{\theta}(x_{i})=\theta^{T}X$和$y_{(i)}$

(1)向量化:计算过程中注意矩阵乘法或是向量乘法的合法性

(2)数据可以用np.matrix存储,但更建议使用np.ndarray

(3)np.matrixnp.ndarray在乘法上有所区别:Numpy中的矩阵乘法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
'''
函数名:
代价函数

参数:
X:矩阵
-可用.shape查看维度(97,2)
y:向量,用numpy.matrix的结构存储
-可用.shape查看维度(97,1)
theta:向量,np.matrix的结构存储
-维度(1,2)

返回值:
代价函数值
'''
def costFunciton(X, y, theta):
inner = np.power(((X * theta.T) - y), 2)
return np.sum(inner) / (2 * len(X))

2.批量梯度下降:

(1)公式:

(2)作用:通过迭代的方式来寻找代价函数最小时的参数($\theta_{j}$)

(3)学习速率:如果过大,会导致无法到达代价值最小点(函数发散或震荡);如果过小,则会使得迭代时间过长。

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
'''
函数名:
梯度下降法
参数:
X:矩阵
y:向量
theta:向量
alpha:学习速率
epoch:迭代次数
返回值:
theta:最后得到的两个参数theta_0,theta_1
cost:最后得到的误差
'''
def gradientDescent(X, y, theta, alpha, epoch):

temp = np.matrix(np.zeros(theta.shape))

cost = np.zeros(epoch) # epoch为迭代次数
m = X.shape[0] # 样本数量

for i in range(epoch):
temp = theta - (alpha / m) * (X.dot(theta.T) - y).T.dot(X)
theta = temp
# 记录一下每次更新后的误差
cost[i] = costFunciton(X, y, theta)

return theta, cost

2*.正规方程法(Normal Equation)

(1)同样也可以用于寻找代价函数最小时候的参数取值,与梯度下降法(Gradient Descent)比较

Aydhpd.md.jpg

(2)公式:$\theta=(X^{T}X)^{-1}X^{T}y$

1
2
3
4
5
'''特征方程'''
def norEquation(X,y):
theta=np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y)

return theta

画图函数

1
2
3
4
5
6
7
8
9
10
11
12
x = np.linspace(
data.Population.min(), data.Population.max(),
100) # 横坐标:linspace(start,end, num)从start开始到end结束,平均分成num份,返回一个数组
f = final_theta[0, 0] + (final_theta[0, 1] * x) # 假设函数

'''函数和散点图'''
plt.figure(figsize=(8, 5))
plt.plot(x, f, 'r', label='Prediction')
plt.scatter(data['Population'], data.Profit, label='Training Data')
plt.xlabel('Population')
plt.ylabel('Profit')
plt.title('Predicted Profit vs. Population Size')

结果图:

AydTnP.md.png

可以看出拟合效果还可以。

1
2
3
4
5
6
7
'''
绘制代价函数与迭代次数的图像
'''
plt.figure(figsize=(8, 5))
plt.plot(np.arange(epoch), cost, 'r')
plt.xlabel('iteration')
plt.ylabel('cost')

结果图:

AydqAS.md.png

随着迭代次数的增加,代价函数值单调递减。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
'''
绘制代价函数3D图像
'''
fig = plt.figure(figsize=(8, 5))
ax = Axes3D(fig)

# 绘制网格
# X,Y value
theta0 = np.linspace(-10, 10, 100) # 网格theta0范围
theta1 = np.linspace(-1, 4, 100) # 网格theta1范围
x1, y1 = np.meshgrid(theta0, theta1) # 画网格
# height value
z = np.zeros(x1.shape)
for i in range(0, theta0.size):
for j in range(0, theta1.size):
t = np.matrix([theta0[i], theta1[j]])
z[i][j] = costFunciton(X, y, t)
# 由循环可以看出,这里是先取x=-10时,y的所有取值,然后计算代价函数传入z的第一行
# 因此在绘图过程中,需要把行和列转置过来
z = z.T
ax.set_xlabel(r'$\theta_0$')
ax.set_ylabel(r'$\theta_1$')
# 绘制函数图像
ax.plot_surface(x1, y1, z, rstride=1, cstride=1)

结果图:

AydLtg.md.png

关于3D图中的Numpy中的Meshgrid

1
2
3
4
5
6
7
'''
绘制等高线图
'''
plt.figure(figsize=(8, 5))
lvls = np.logspace(-2, 3, 20)
plt.contour(x1, y1, z, levels=lvls, norm=LogNorm()) # 画出等高线
plt.plot(final_theta[0, 0], final_theta[0, 1], 'r', marker='x') # 标出代价函数最小值点

结果图:

AydOhQ.md.png

线性回归-多变量(multiple variables)

特征缩放(Feature scaling)

也叫均值归一化

(1)公式:$x=\frac{x-\mu}{s_{1}}$,其中$\mu$为$x$的均值,$s_{1}$为$x$的最大值减去最小值,或者使用标准差。

(2)作用:在多个特征值情况下,如果某个特征值$x_{i}$的取值范围和另一个特征值$x_{j}$的取值范围相差太大,会减慢梯度下降的速度,因此需要用特征缩放,将不同特征值的取值限定在差不多的范围内。

e.g. $x_{1}$为房子的面积,取值范围0~2000;$x_{2}$为卧室的数量,取值0-5;那么对二者使用特征缩放,可得:

ADOqIS.md.png

(3)代码:

1
2
data = (data - data.mean()) / data.std()  
# 除数可以用标准差也可以用max-min,因为pandas方便,所以使用标准差

参考资料

[1] 机器学习 | 吴恩达机器学习第一周学习笔记

[2] 机器学习 | 吴恩达机器学习第二周编程作业(Python版)

[3] 吴恩达机器学习作业Python实现(一):线性回归

代码

scp-1024/Coursera-ML-Ng

-------------End-------------
梦想总是要有的,万一有人有钱呢?