【机器学习】线性回归

在吴恩达的机器学习的视频中有一个房价预测问题,通过真实的一些房价的数据去预测房价。对此,可以根据y=wx+b回归函数从房子的面积对价格进行预测。现在有一批波士顿房价的数据,其对房价的影响因素有13个,我们取出第五个房价的大小作为影响因素去预测房价。

简单线性回归

对于房价预测问题,我们需要建立一个数学模型去拟合这些分布不均匀数据,使得不同的房子的面积对应不同的房价。给出的数学模型为
\begin{aligned}
y = θ_0 + θ_1*x
\end{aligned}
对于\(θ_0\)\(θ_1\)称之为模型参数,已知x,也就是求得\(θ_0\)\(θ_1\)我们就可以知道对应面积的房子的价格。当得到最佳的\(θ_0\)\(θ_1\)之后,也就得到了最拟合实际的房价y。
那么如何求得最合适的\(θ_i\)呢,根据上面的散点图作为训练数据,我们选择的参数决定了我们得到的直线相对于我们的训练集的准确程度,模型所预测的值与训练集中实际值之间的差距越小,则代表我们获取的\(θ_i\)越好。所以在线性回归中我们要解决一个最小化的问题,而模型所预测的值与训练集中实际值之间的差距就是建模误差
我们的目标便是选择出可以使得建模误差的平方和能够最小的模型参数,即使得代价函数(公式如下)最小。

\[J(\Theta _0,\Theta _1) = \frac{1}{2m}\sum_{i=1}^{m}(h_\Theta(x^{(i)}) - y^{(i)} )^2 \]

m是样本总数,1/2m是将这个表达式简化。
\begin{aligned}
h_\Theta (x^i) = \Theta _0 + \Theta _1x
\end{aligned}
所以这个问题就是:找到能使我们训练集中的预测值和真实值的差的平方的和的1/2m最小的\(0_1\)\(0_0\)值。上述公式也成为平方误差函数,也就我们所需要用到的损失函数。
\(J(θ_0,θ_1)\)越小,也就是反应了预测的房价和真实房价的误差也就越小,我们通过用梯度下降的方法去求\(J(θ_0,θ_1)\)得最小值。

梯度下降法求最小值

度下降背后的思想是:开始时我们随机选择一个参数的组合(θ0,θ1,...,θn),计算代价函数,然后我们寻找下一个能让代价函数值下降最多的参数组合。我们持续这么做直到到到一个局部最小值(local minimum),因为我们并没有尝试完所有的参数组合,所以不能确定我们得到的局部最小值是否便是全局最小值(global minimum),选择不同的初始参数组合,可能会找到不同的局部最小值。
想象一下你正站立在山的这一点上,站立在你想象的公园这座红色山上,在梯度下降算法中,我们要做的就是旋转360度,看看我们的周围,并问自己要在某个方向上,用小碎步尽快下山。这些小碎步需要朝什么方向?如果我们站在山坡上的这一点,你看一下周围,你会发现最佳的下山方向,你再看看周围,然后再一次想想,我应该从什么方向迈着小碎步下山?然后你按照自己的判断又迈出一步,重复上面的步骤,从这个新的点,你环顾四周,并决定从什么方向将会最快下山,然后又迈进了一小步,并依此类推,直到你接近局部最低点的位置。
梯度下降的公式

\[\Theta _j := \Theta _j - \alpha \frac{\partial J(\Theta _0,\Theta _1)}{\partial \Theta _j} (j = 0 ,j = 1) \]

其中α是学习率(learning rate),它决定了我们沿着能让代价函数下降程度最大的方向向下迈出的步子有多大,在批量梯度下降中,我们每一次都同时让所有的参数(\(θ_0\),\(θ_1\))减去学习速率乘以代价函数的导数。所以梯度下降中,我们要更新\(θ_0\)\(θ_1\),当 j=0 和 j=1 时,就会产生更新,所以你将更新\(J_θ0\)\(J_θ1\)。如果你要更新这个等式,你需要同时更新θ0和θ1,也就是

\[\Theta _0 := \Theta _0 - \alpha \frac{\partial J(\Theta _0,\Theta _1)}{\partial \Theta _0} (j = 0 )\\ \Theta _1 := \Theta _1 - \alpha \frac{\partial J(\Theta _0,\Theta _1)}{\partial \Theta _1} (j = 1 ) \]

那么接下来对θ赋值,使得J(θ)按梯度下降最快方向进行,一直迭代下去,最终得到局部最小值。其中α是学习率。它决定了我们沿着能让代价函数下降程度最大的方向向下迈出的步子有多大。由于导数总是变化最快的方向,所以对\(J(θ)\)来说,也就是它变化最快的方向。
梯度下降法的更新规则
求导的目的,也就是取的该点的斜率,就是红色直线与函数相切于这一点,这条刚好与函数曲线相切的直线的斜率正好是这个三角形的高度除以这个水平长度,现在,这条线有一个正斜率,也就是说它有正导数,因此会得到得到的新的\(θ_1\)\(θ_1\)更新后等于\(θ_1\)减去一个正数乘以α。
但是步长α不能太大也不能太小
如果α太小了,也是就学习速率太小,这样就需要很多步才能到达最低点。
如果α太大,梯度下降法可能会越过最低点,甚至可能无法收敛,甚至发散。
如果\(θ_1\)已经在局部最低点了,这时候求出来的斜率为0,那么它使得\(θ_1\)不再改变,也就是新的\(θ_1\)等于原来的\(θ_1\),所以这个时候梯度函数就什么都没有做。同时呢,当α固定,斜率越大,\(θ_1\)更新的幅度也就越大,也就走的越快,斜率越小,更新幅度就小,也就走的慢,这个时候就有可能达到局部最优解。也就是在梯度下降法中,当接近局部最低点时,梯度下降法会自动采取更小的幅度。没有没有必要再这种情况再去减小α。
梯度下降算法以用来最小化任何代价函数J,不只是线性回归中的代价函数J。
现在运用梯度下降算法去求线性回归模型的最优解了。
梯度下降算法

\begin{aligned}
\Theta _j := \Theta _j - \alpha \frac{\partial J(\Theta _0,\Theta _1)}{\partial \Theta _j} (j = 0 ,j = 1)
\end{aligned}

线性回归函数
\begin{aligned}
h_\Theta (x) = \Theta _0 + \Theta _1x
\end{aligned}
线性回归的损失函数

\[J(\Theta _0,\Theta _1) = \frac{1}{2m}\sum_{i=1}^{m}(h_\Theta(x^{(i)}) - y^{(i)} )^2 \]

对损失函数求导,用梯度下降法去更新\(θ_j\)的值
当j=0,求导

\[\frac{\partial }{\partial \Theta _0}J(\Theta _0,\Theta _1) = \frac{1}{m}\sum_{i=1}^{m}(h_\Theta (x^{(i)})-y^{(i)}) \]

当j=1,求导

\[\frac{\partial }{\partial \Theta _1}J(\Theta _0,\Theta _1) = \frac{1}{m}\sum_{i=1}^{m}(h_\Theta (x^{(i)})-y^{(i)})(x^{(i)}) \]

由于在梯度下降的每一步中,我们都用到了所有的训练样本,所以也叫批量梯度下降。

对于线性回归,我们可以给\(0_0\)乘以一个常数。那么公式就变成下面这样

\[h_\Theta (x) = \Theta _0 x_0+ \Theta _1 x_1(x_0 = 1) \]

那么对于\(x_i\),它的梯度下降就变成如下

\[\Theta _j:=\Theta _j - \alpha \frac{1}{m}\sum_{i=1}^{m}(h_\Theta (x^{(i)})-y^{(i)})(x_j^{(i)}) \]

代码实现

python

# _*_ encoding=utf8 _*_

import numpy as np
import matplotlib.pyplot as plt
import codecs

def loadData():
    '''
    加载数据
    :param filename:
    :return:
    '''
    data = []
    with codecs.open("./data/ex1data1.txt","r","utf-8") as fr:
        for index,line in enumerate(fr):
            temp_label = []
            temp_train = []
            lineArr = line.strip().split(",")
            temp = []
            for i in range(2):
                temp.append(float(lineArr[i]))
            data.append(temp)
    return data

def costFuntion(X_test,Y_test,theta):
    shape = X_test.shape[0]  # 97
    return 0.5*np.sum(np.square(X_test.dot(theta) - Y_test))/shape

def gradDescent(X_test,Y_test):
    iters = 1000
    alpha = 0.01
    loss = []
    theta = np.array([[0.001],[0.001]])
    num_X = X_test.shape[0] #97
    for i in range(iters):
        theta = theta - alpha* X_test.T.dot(X_test.dot(theta) - Y_test) / num_X
        cost = costFuntion(X_test, Y_test, theta)
        loss.append(cost)

    return theta, loss


data = loadData()
data = np.array(data)
print(data)
print(data.shape)

X = data[:,:-1]
Y = data[:,-1:]

# 查看数据
plt.scatter(X,Y,color='red')
plt.xlabel('train')
plt.ylabel('label')
plt.show()


num_shape = X.shape[0]
ones = np.ones((num_shape, 1))
# 补1data[:,:-1]
X = np.hstack((ones,X))
print(Y) #theta0 theta1
w = np.zeros((2,1))

# X = [97,2]  Y[97,1]  得到了最优theta参数
theta,loss = gradDescent(X,Y)
print(theta)

#查看拟合的曲线
plt.scatter(X[:,1],Y,color = 'red')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(X[:,1],X.dot(theta),'-',color = 'yellow')
plt.show()

#查看损失函数的变化
plt.plot(loss)
plt.xlabel('iter')
plt.ylabel('loss')
plt.show()

tensorflow

# _*_ encoding=utf8 _*_

# 导入需要的python库
import tensorflow as tf
import matplotlib.pyplot as plt



# 获取波士顿房价数据集,并且房间的大小去预测波士顿房价
data = tf.contrib.learn.datasets.load_dataset('boston')
X_train,Y_train = data.data[:,5],data.target # 取出第五个因数作为因变量
# 查看取出来的训练数据和形状,[506,1]
print(X_train)
print(X_train.shape)

# 获取多少个训练数据样本
n_sample = len(X_train)


# 为训练数据声明占位符
X = tf.placeholder(tf.float32,name = "X")
Y = tf.placeholder(tf.float32,name = "Y")

# 创建权重和偏置项并且都置为0
b = tf.Variable(0.0)
w = tf.Variable(0.0)

# 定义用于房价预测的线性回归模型
line_model = X * w + b
# 计算预测的房价和取出来的target的差值,作为损失
loss = tf.square(Y - line_model,name= "loss")
# 采用梯度下降优化器优化权重
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize((loss))


init = tf.global_variables_initializer()

total = []
with tf.Session() as sess:
    sess.run(init)

    # 声明图
    writer = tf.summary.FileWriter("graphs", sess.graph)
    # 100 迭代
    for i in range(100):
        total_loss = 0
        for x,y in zip(X_train,Y_train):
            # 这儿计算出来的是每一次迭代每一个训练数据的预测值和真实值的损失,
            _,_loss = sess.run([optimizer,loss],feed_dict={X:x,Y:y})
            # 将每一次损失相加
            total_loss += _loss

        # 得到每一次迭代的损失
        total.append(total_loss / n_sample)
        print('Epoch {0}: Loss {1}'.format(i,total_loss/n_sample))
        writer.close()

    # 查看训练的参数
    b_value,w_value = sess.run([b,w])
    print(w_value)
    print(b_value)
    # 查看结果 Y_pred就是用训练的参数得到的线性回归模型
    Y_pred = X_train * w_value + b_value
    print("train finished")

    # 蓝色的数原始数据,红色的线就是训练出来的线性模型
    plt.plot(X_train,Y_train, "boston", label = "data")
    plt.plot(X_train,Y_pred,'r',label = "predicted data")
    plt.legend()
    plt.show()
    # 查看训练过程中loss的变化
    plt.plot(total)
    plt.show()

在吴恩达的机器学习的视频中,通过真实的一些房价的数据去预测房价。除了房子的面积之外,还有房间数楼层等因素,都会影响房子的价格,对此,可根据多元回归函数从房子多方面的情况对价格进行预测。对这些变量特征描述为\((x_1,x_2,...,x_n)\)

多元线性回归

从单变量线性回归我们可以推出,多变量线性回归的函数可以表示为
\begin{aligned}
h_\Theta (x) = \Theta _0 + \Theta _1x_1 + \Theta _2x_2 + ... + \Theta _nx_n
\end{aligned}
还是和之前一样,将\(x_0\)设置为1,此时模型的参数就是一个n+1维的向量 ,任何一个训练实例也都是n+1维的向,特征矩阵X的维度是 m*(n+1)。因此公式可以简化为

\[h_\Theta (x) = \Theta ^T x \]

其中\(θ^T\)表示参数矩阵θ的转置矩阵。
从之前的单线性回归可以得到,多线性回归的损失函数为

\[J(\Theta _0\Theta _1...\Theta _n) = \frac{1}{2m}*\sum_{i=1}^{m}(h_\Theta (x^i)-y^i)^2 \]

多元线性回归表示为:

\[h_\Theta (x) = \Theta ^T x = \Theta _0x_0 + \Theta _1x_1 + \Theta _2x_2 + ... + \Theta _nx_n \]

同时,和单变量线性回归一样,我们需要根据梯度下降找到使得代价函数最小的参数。该损失函数的梯度下降公式。

\[\Theta _j:=\Theta _j - \alpha \frac{\partial J(\Theta _0,\Theta _1...\Theta _n)}{\partial \Theta _j} \]

也就是

\[\Theta _j =\Theta _j - \alpha \frac{\partial }{\partial \Theta _j}\frac{1}{2m}\sum_{i=1}^{m}(h_\Theta (x^{(i)})-y^{(i)})^2 \]

对其进行求导

\[\Theta _j:=\Theta _j - \alpha \frac{1}{m}\sum_{i=1}^{m}(h_\Theta (x^{(i)})-y^{(i)})(x_j^{(i)}) \]

对于\(θ_0,θ_1...θ_n\),我们都有上述梯度下降去更新θ的值,并且所有的参数进行同时更新。直到函数收敛。

特征缩放

对多维特征的时候,时常会发现这些特征的尺度相差很大,他们没有一个统一的单位,比如:房屋的尺寸可能是0-2000平凡英尺,但是房间的数量就1-5间,这样的话代价函数打的等高线图就呈现椭圆形,很难收敛,一般解决的办法是将这些特征缩放到-1到1。
对于数据的均值归一化,可以下面的方法
第一种
\begin{aligned}
X = \frac{X - \mu _n}{S_n}
\end{aligned}
其中是\(\mu _n\)平均值,\(S_n\)是标准差,分母用最大值减去最小值也是可以的,$X_1 = \frac{size - 1000}{2000} \(,\)X_2 = \frac{roomnum - 2}{5}$。

代码实现

python

# _*_ encoding=utf8 _*_

import numpy as np
import matplotlib.pyplot as plt
import codecs

def loadData():
    '''
    加载数据
    :param filename:
    :return:
    '''
    data = []
    # 两个特征,第一个是房子大小,第二个是房间数
    with codecs.open("./data/ex1data2.txt","r","utf-8") as fr:
        for index,line in enumerate(fr):
            lineArr = line.strip().split(",")
            temp = []
            for i in range(3):
                temp.append(float(lineArr[i]))
            data.append(temp)
    return data

def dataNormalize(train_data):
    '''
    均值归一化
    :param train_data:
    :return:
    '''
    mean = np.mean(train_data, axis=0) # 对列求均值
    std = np.std(train_data,axis=0)

    norm_data = train_data - mean / std
    return  norm_data

def costFuntion(X_test,Y_test,theta):
    shape = X_test.shape[0]
    return 0.5*np.sum(np.square(X_test.dot(theta) - Y_test))/shape

def gradDescent(X_test,Y_test,theta):
    iters = 1000
    alpha = 0.01
    loss = []
    num_X = X_test.shape[0] # 47
    for i in range(iters):
        theta = theta - alpha* X_test.T.dot(X_test.dot(theta) - Y_test) / num_X
        cost = costFuntion(X_test, Y_test, theta)
        loss.append(cost)

    return theta, loss


data = loadData()
data = np.array(data)
print(data.shape)
X = data[:,:-1] # 两个特征
Y = data[:,-1:] #


norm_data = dataNormalize(X) # 归一化数据
num_shape = X.shape[0]
ones = np.ones((num_shape, 1))
X = np.hstack((ones, norm_data))
# 3 = data.shape[1]
print(X.shape[1])
w = np.zeros((X.shape[1],1))

# X = [97,2]  Y[97,1]  得到了最优theta参数
theta,loss = gradDescent(X,Y,w)
print(theta)

#查看损失函数的变化gradDescent
plt.plot(loss)
plt.xlabel('iter')
plt.ylabel('loss')
plt.show()

tensorflow

# _*_ encoding=utf8 _*_


import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt



def normalize(X):
    '''
    数据归一化,由于每个数据的变化范围不同,比如面积可能在50-300之间,但是里市中心的距离就是0-1000
    采用归一化的方法,归一化原始数据经过数据标准化处理后,使得各指标处于同一数量级,进行综合对比评价。
    :param X:
    :return: 归一化数据之后的矩阵
    '''
    mean = np.mean(X)  # np.mean(a, axis=0) # axis=0,计算每一列的均值 axis=1,计算每一行的均值
    std = np.std(X)  # 计算全局标准差 np.std(a, axis=0) # axis=0计算每一列的标准差 axis=1计算每一行的标准差
    X = (X - mean) / std # 矩阵X每一个元素都减去平均值
    return X

def append_bias_reshape(features,labels):
    '''
    该函数的作用是添加一个额外的固定输入值将权重和偏置结合起来
    :param features:
    :param labels:
    :return:
    '''
    m = features.shape[0]
    n = features.shape[1]
    # np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等。
    # np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。
    # 对特征矩阵进行处理,在特征矩阵每一行前面都补上一个1,特征矩阵从[m,n]扩展为[m,n+1]
    x = np.reshape(np.c_[np.ones(m),features],[m, n+1])
    y = np.reshape(labels,[m,1])
    return x, y


# 获取波士顿房价的数据
data = tf.contrib.learn.datasets.load_dataset('boston')
X_train,Y_train = data.data,data.target
# 归一化数据
X_train = normalize(X_train)
# 将权重和偏置结合起来  这种方式明显可以加快运算
X_train,Y_train = append_bias_reshape(X_train,Y_train)
m = len(X_train)  #训练集样本数 506
n = 13 + 1 #特征数加 偏移 X_train.shape[0]
# n = 13


# 声明占位符
X = tf.placeholder(dtype = tf.float32,shape=[m,n],name = "X")
Y = tf.placeholder(tf.float32,name = "Y")

# 初始化权重w
# b = tf.Variable(tf.random_normal([m]))
w = tf.Variable(tf.random_normal([n,1]))

# X w叉乘[m,n+1]  [n+1,1] 相当于刚刚用1补充的那个位置,1乘以任何数等于任何数,起到一个偏置的作用
# Y_ = tf.add(tf.matmul(X ,w), b)
Y_ = tf.matmul(X ,w)
loss = tf.reduce_mean(tf.square(Y - Y_,name = "loss"))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize((loss))


init = tf.global_variables_initializer()
total = []

with tf.Session() as sess:
    sess.run(init)

    writer = tf.summary.FileWriter("line_recogniton_graphs", sess.graph)
    for i in range(500):
        for x,y in zip(X_train,Y_train):
            _,_loss = sess.run([optimizer,loss],feed_dict={X:X_train,Y:Y_train})
            total.append(_loss)
            print('epoch {0}: Loss {1}'.format(i,_loss))
        writer.close()

    # w_value,b_value = sess.run([w, b])
    w_value = sess.run([w])
    print(w_value)
    # print(b_value)
    # 查看训练过程损失的变化
    plt.plot(total)
    plt.show()


    # 通过模型学习到的参数来进行预测
    N = 500
    X_test = X_train[N,:]
    X_test = X_test.reshape(1,n)
    predict = np.matmul(X_test,w_value).round(1)
    # predict = np.matmul(X_test,w_value) + b_value[0]
    print(predict)
    print("预测房价为:${0} 真实房价为: ${1}".format(predict*1000, Y_train[N]*1000))

参考

吴恩达机器学习课程

posted @ 2020-03-09 22:37  晓看天色暮看云  阅读(302)  评论(0编辑  收藏  举报