[机器学习] 房屋价格预测(补充)

前言

在之前有许多关于线性回归-房屋价格预测的优秀文章, 这篇blog更适合看了吴恩达机器学习,想通过Python实现线性回归。我试着写了代码,在过程中遇到一些问题,有的是公式问题,有的是关于numpy库的问题,有的blog在这些方面不太完善,我在这里会一一列举出来。

项目地址

暂无

参考连接

吴恩达机器学习 线性回归

前导知识

需要知道关于假设函数
H ( x ) = Θ 0 + Θ 1 x H(x) = Θ_0 + Θ_1x H(x)=Θ0+Θ1x
需要知道关于代价函数
J ( Θ 0 , Θ 1 ) = ∑ i = 1 m [ h ( x ( i ) ) − y ( i ) ] 2 2 m J(Θ_0, Θ_1) ={\sum_{i=1}^m[h(x^{(i)}) - y^{(i)}]^2\over 2m} J(Θ0,Θ1)=2mi=1m[h(x(i))y(i)]2
需要知道关于多元代价函数的偏导数
J ( Θ j ) ′ = ∑ i = 1 m ( h ( x ( i ) ) − y ( i ) ) x ( i ) m J(Θ_j) '={\sum_{i=1}^m(h(x^{(i)}) - y^{(i)}) x^{(i)}\over m} J(Θj)=mi=1m(h(x(i))y(i))x(i)

需要知道关于均值归一化
X ( i ) = X ( i ) − μ X m a x − X m i n X^{(i)} = {X^{(i)} - μ\over X_{max} - X_{min}} X(i)=XmaxXminX(i)μ

还需要知道一些numpy的基本用法, 以及matplotlib的简单使用。

正文

一、加载数据集
def load_file(filepath):
    """
        加载文件, 数据类型默认float64
    :param filepath: 文件
    :return: m * n 的 array, m 为数据条目, n 为特征条目
    """
    return np.loadtxt(filepath, delimiter=',', dtype=np.float64)

load_file函数返回np.array数据类型的数据, 例如:
[ 2104 399900 1600 329900 1416 232000 ] \left[ \begin{matrix} 2104 & 399900\\ 1600 & 329900 \\ 1416 & 232000 \end {matrix} \right] 210416001416399900329900232000
以 np.array 表示为:
[ [ 2104 , 399900 ] [ 1600 , 329900 ] [ 1416 , 232000 ] ] [[2104, 399900] \\ [1600, 329900] \\ [1416, 232000]] [[2104,399900][1600,329900][1416,232000]]
是一个 3 ∗ 2 3*2 32的矩阵

二、绘制数据
import numpy as np
from matplotlib import pyplot as plt

def plot_house_price(data, theta = None):
    """
        绘制房屋面积与价格的离散图
    :param data: 为 np.array 类型 含两列数据, 第0列指面积, 第1列指房屋价格
    :param theta: [Θ₀, Θ₁] 拟合函数系数
    """
    # 设置 x and y 轴的标签 与 该图形的标题
    plt.xlabel("area")
    plt.ylabel("price")
    plt.title("Houses sale instance")
    # 绘制圆点, data[i][0] 为面积, data[i][1] 为价格
    plt.scatter(data[:, 0], data[:, 1], s=20)

    # 如果传入theta列表, 表示需要绘制拟合函数
    if theta is not None:
        # 设置显示范围
        x_axis = np.linspace(-np.max(data[:, 0]) * 1.2, np.max(data[:, 0]) * 1.2)
        # h(x) = Θ₀ + Θ₁ x
        y_axis = theta[0] + theta[1] * x_axis
        # 绘制拟合函数
        plt.plot(x_axis, y_axis, "g-")

    # 显示
    plt.show()

其中, 一些绘图的操作便不再赘述。值得注意的是 d a t a [ : , 0 ] data[:, 0] data[:,0],这是一个切片操作,逗号前后分别是行索引列索引的切片,那么就这句意为“取矩阵的第0列(所有行)”。
在这里插入图片描述

三、对数据集归一化

归一化的作用是为了使函数快速收敛。如果对于差异度很大的数据集,如果不归一化,使用梯度下降算法训练模型时,可能导致函数不收敛,也就无法训练出模型。(本例即是)

def mean_normalization(data):
    """
        data 为 np.array格式 含两列数据, 第0列指面积, 第1列指房屋价格
        均值归一化
    :return:
    """

    # col_mean 即 列的均值; col_std 即 列的标准差;
    # 参数 0 代表列; 若求行均值或标准差 传入参数 1 即可
    col_mean = np.mean(data, 0)
    col_max = np.max(data, 0)
    col_min = np.min(data, 0)

    # shape 返回 维度的维数 即
    # l = [[1, 2, 3], [4, 5, 6]] => shape[0] = 2, shape[1] = 3
    for i in range(data.shape[1]):
        """
            shape[1] 返回列数
             Xᵢ = (Xᵢ - μ) / (Xₘₐₓ - Xₘᵢₙ)
            其中, Xᵢ 为第i个特征, μ 是当前特征的均值, (Xₘₐₓ - Xₘᵢₙ) 是当前特征的范围
        """

        # 让 当前特征中每个特征值都执行归一化操作
        # 均值归一化, 一个特征即矩阵中一列
        data[:, i] = (data[:, i] - col_mean[i]) / (col_max[i] - col_min[i])
# 这段代码,虽无必要说明,但还是贴到这里
# 加载资源,然后绘制房屋价格点图,然后执行归一化后,再绘制
if __name__ == '__main__':
    data = load_file("data.txt")
    plot_house_price(data)
    print(data)
    mean_normalization(data)
    plot_house_price(data)
    print(data)

在这里插入图片描述
对比没有归一化之前的图像, 两者差别不大。

番外:绘制J函数

停停… 在对数据集归一化后,就可以执行梯度下降算法,解出Θ值。但是,我在做的过程中,对代价函数J非常感兴趣,所以耗费一点时间绘制出来。这里附带一个推导:

先说结论,
J ( Θ 0 , Θ 1 ) = ∑ i = 1 m [ h ( x i ) − y i ] 2 2 m = K ( x Θ − y ) 2 2 m = K A 2 2 m J(Θ_0, Θ_1) ={\sum_{i=1}^m[h(x^i) - y^i]^2\over 2m} ={K(xΘ - y)^2\over 2m} = {KA^2\over 2m} J(Θ0,Θ1)=2mi=1m[h(xi)yi]2=2mK(xΘy)2=2mKA2
推导如下,
x x x为列向量,其值有 [ x ( 1 ) x ( 2 ) x ( 3 ) x ( 4 ) … x ( m ) ] \left[ \begin{matrix} x^{(1)}\\ x^{(2)}\\ x^{(3)}\\ x^{(4)} \\ \dots \\ x^{(m)}\end{matrix}\right] x(1)x(2)x(3)x(4)x(m)
y y y同为列向量,其值有 [ y ( 1 ) y ( 2 ) y ( 3 ) y ( 4 ) … y ( m ) ] \left[ \begin{matrix} y^{(1)}\\ y^{(2)}\\ y^{(3)}\\ y^{(4)}\\ \dots \\ y^{(m)}\end{matrix}\right] y(1)y(2)y(3)y(4)y(m)
系数矩阵 Θ Θ Θ为,
[ Θ 0 Θ 1 ] \left[ \begin{matrix} Θ_0\\ Θ_1\end{matrix}\right] [Θ0Θ1]
我们知道假设函数 h ( x ) = Θ 0 + Θ 1 x h(x) = Θ_0 + Θ_1x h(x)=Θ0+Θ1x,那么 h ( x i ) = Θ 0 + Θ 1 x i h(x^i) = Θ_0 + Θ_1x^i h(xi)=Θ0+Θ1xi,有 h ( x i ) h(x^i) h(xi)的矩阵形式:
[ Θ 0 + Θ 1 x ( 1 ) Θ 0 + Θ 1 x ( 2 ) Θ 0 + Θ 1 x ( 3 ) Θ 0 + Θ 1 x ( 4 ) … Θ 0 + Θ 1 x ( m ) ] \left[ \begin{matrix} Θ_0 + Θ_1x^{(1)}\\ Θ_0 + Θ_1x^{(2)}\\ Θ_0 + Θ_1x^{(3)}\\ Θ_0 + Θ_1x^{(4)} \\ \dots \\ Θ_0 + Θ_1x^{(m)} \end{matrix}\right] Θ0+Θ1x(1)Θ0+Θ1x(2)Θ0+Θ1x(3)Θ0+Θ1x(4)Θ0+Θ1x(m)
所以,为了运算方便,只需给 x x x加一列 1 1 1,使得 x ′ x' x
[ 1 x ( 1 ) 1 x ( 2 ) 1 x ( 3 ) 1 x ( 4 ) … … 1 x ( m ) ] \left[ \begin{matrix} 1 & x^{(1)}\\ 1 & x^{(2)}\\ 1 & x^{(3)}\\ 1 & x^{(4)} \\ \dots & \dots \\ 1 & x^{(m)} \end{matrix}\right] 11111x(1)x(2)x(3)x(4)x(m),那么,可以推出 h ( x i ) = x ′ Θ h(x^i) = x'Θ h(xi)=xΘ(注意, Θ Θ Θ是系数矩阵),则有矩阵 A = h ( x i ) − y i = x ′ Θ − y A = h(x^i) - y^i = x'Θ-y A=h(xi)yi=xΘy,得出矩阵 B B B B = A 2 = [ h ( x i ) − y i ] 2 B = A^2 = [h(x^i) - y^i]^2 B=A2=[h(xi)yi]2
如何求解 ∑ i = 1 m \sum_{i=1}^m i=1m呢?这里, 可以利用矩阵乘法的性质:
已知矩阵B为
[ A 2 ( 1 ) A 2 ( 2 ) A 2 ( 3 ) A 2 ( 4 ) … A 2 ( m ) ] \left[ \begin{matrix} A^{2(1)}\\ A^{2(2)}\\ A^{2(3)}\\ A^{2(4)}\\ \dots \\ A^{2(m)}\end{matrix}\right] A2(1)A2(2)A2(3)A2(4)A2(m)
,取矩阵 K K K
[ 1 1 … 1 ] \left[ \begin{matrix} 1& 1 & \dots & 1\end{matrix}\right] [111]
, 大小为 1 ∗ m 1 *m 1m, 矩阵 B B B的大小为 m ∗ 1 m * 1 m1,则 K ∗ B = S K * B = S KB=S, S S S ∑ i = 1 m [ h ( x i ) − y i ] 2 {\sum_{i=1}^m[h(x^i) - y^i]^2} i=1m[h(xi)yi]2

def plot_J_function(sr_x, sr_y):
    """
        绘制代价函数J
    :param sr_x: 房屋面积, 归一化后的数据
    :param sr_y: 房屋价格, 同归一化后的数据
    :return: 
    """
    
    # 样本数量
    m = sr_x.shape[0]

    # 扩展sr_x
    sr_x = np.column_stack((np.ones(len(sr_x)), sr_x))
    # 全 1 行向量
    K = np.array([np.ones(sr_x.shape[0])])

    # X and Y轴 刻度
    delta = 1
    # X, Y 的显示范围
    X = np.arange(-10, 10, delta)
    Y = np.arange(-10, 10, delta)

    Z = []
    for t0 in X:
        Zl = []
        for t1 in Y:
            theta = np.array([[t0, t1]]).T
            J = np.dot(K,
                       np.power(np.dot(sr_x, theta) - sr_y, 2)
                ) / (2 * m)
            Zl.append(J[0][0])
        Z.append(Zl)
    Z = np.array(Z)

    # 扩充, 此时X,Y分别是一个矩阵, 内含所有可能的取样点
    X, Y = np.meshgrid(X, Y)
    # 显示
    ax = plt.axes(projection='3d')
    ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=plt.cm.coolwarm, edgecolor='none')
    ax.set_xlabel('theta_0')
    ax.set_ylabel('theta_1')
    ax.set_zlabel('J')
    plt.show()
if __name__ == '__main__':
    data = load_file("data.txt")
    plot_house_price(data)
    mean_normalization(data)
    plot_house_price(data)
    x = np.array([data[:, 0]]).T
    y = np.array([data[:, 1]]).T
    plot_J_function(x, y)

在这里插入图片描述

四、梯度下降求解Θ

要找到 m i n m i z e J ( Θ 0 , Θ 1 ) minmize J(Θ_0, Θ_1) minmizeJ(Θ0,Θ1)的极小值点,只需要求出 J J J的各项偏导, 再令其偏导数 = 0,就可以解出 Θ 0 , Θ 1 , … , Θ n Θ_0, Θ_1, \dots, Θ_n Θ0,Θ1,,Θn

有J的偏导数,
J ( Θ j ) ′ = ∑ i = 1 m ( h ( x ( i ) ) − y ( i ) ) x ( i ) m J(Θ_j) '={\sum_{i=1}^m(h(x^{(i)}) - y^{(i)})x^{(i)}\over m} J(Θj)=mi=1m(h(x(i))y(i))x(i),规定 x ( 0 ) = 1 x^{(0)} = 1 x(0)=1, 则有
J ( Θ 0 ) ′ = ∑ i = 1 m ( h ( x ( i ) ) − y ( i ) ) m J ( Θ 1 ) ′ = ∑ i = 1 m ( h ( x ( i ) ) − y ( i ) ) x ( i ) m J(Θ_0)' ={\sum_{i=1}^m(h(x^{(i)}) - y^{(i)})\over m} \\ \\J(Θ_1)' = {\sum_{i=1}^m(h(x^{(i)}) - y^{(i)})x^{(i)}\over m} J(Θ0)=mi=1m(h(x(i))y(i))J(Θ1)=mi=1m(h(x(i))y(i))x(i)绘制J函数部分有讲到,矩阵 A = h ( x i ) − y i = x ′ Θ − y A = h(x^i) - y^i = x'Θ-y A=h(xi)yi=xΘy K ∗ B = S K * B = S KB=S,而 K K K是全 1 1 1的行向量,其实,在这里,只需将 K K K替换成 x T x^T xT即可,所以有
∑ i = 1 m ( h ( x ( i ) ) − y ( i ) ) x ( i ) = x T ( x ′ Θ − y ) \sum_{i=1}^m(h(x^{(i)}) - y^{(i)})x^{(i)} = x^T(x'Θ - y) i=1m(h(x(i))y(i))x(i)=xT(xΘy),这部分可以自己尝试着证明一下。
所以,
Θ j = Θ j − α m J ( Θ j ) ′ Θ_j = Θ_j - {α\over m}J(Θ_j)' Θj=ΘjmαJ(Θj) 随着迭代次数的增多, J ( Θ j ) ′ 慢 慢 趋 向 0 J(Θ_j)' 慢慢趋向0 J(Θj)0,最后 Θ j Θ_j Θj便稳定下来。

def gradient_descent(sr_x, sr_y, theta, alpha, iter_steps):
    """
        梯度下降
    :param sr_x: 列向量
    :param sr_y: 列向量
    :param theta: 列向量
    :param alpha: 学习率
    :param iter_steps: 迭代次数
    :return: theta
    """
    theta0 = theta[0][0]
    theta1 = theta[1][0]

    # 样本数量
    m = sr_x.shape[0]
    # 扩展sr_x
    X = np.column_stack((np.ones(len(sr_x)), sr_x))
    xT = sr_x.T
    K = np.array([np.ones(sr_x.shape[0])])

    for i in range(iter_steps):
        # XΘ-Y
        hypothesis = np.dot(X, theta)
        loss = hypothesis - sr_y

        # update theta0
        temp0 = theta[0][0] - (alpha / m) * np.dot(K, loss)
        # update theta1
        temp1 = theta[1][0] - (alpha / m) * np.dot(xT, loss)
        theta[0][0] = temp0
        theta[1][0] = temp1

    return theta

if __name__ == '__main__':
    data = load_file("data.txt")
    plot_house_price(data)
    mean_normalization(data)
    plot_house_price(data)
    theta = np.array([[1., 1.]]).T
    theta = gradient_descent(x, y, theta, 0.05, 500)
    print(theta)
    plot_house_price(data, [theta[0][0], theta[1][0]])

在这里插入图片描述

小结

本来还有一些东西要讲,但是我换了个思路写代码,那些小疑问也就不需要再说了。例如在梯度下降时,别人的代码是这样的:

    xTrains = x.transpose()
    for i in range(0, maxIterations):
        hypothesis = np.dot(x, theta)
        loss = hypothesis - y
        gradient = np.dot(xTrains, loss) / m
        theta = theta - alpha * gradient
    return theta

与我的差别不大,利用的是一维数组点乘(不知道起什么名…),在我第一次看到这段代码,并运行出结果时感觉非常惊讶,可能是不太熟悉numpy的原因,这段代码中的theta[1, 1] – 一个list。操作有点迷,但是理解之后便觉得非常好用。
解释 - numpy数组与矩阵乘法
我的代码则按照吴恩达老师的视频讲解那样,先更新theta0,再更新theta1,最后得出结果。

第一次写线性回归时,还有个疑问(没认真听的后果),theta的初始值该为什么?,这个问题想明白也会觉得‘不过如此’。
当然还有一个关于np.array的问题也提点一句,np.array([1, 2])返回的是一维数组,而不是向量;np.array([[1, 2]])这才是行向量。可以用shape属性输出就明白了。

结疑

标签

***机器学习,吴恩达视频,房屋价格预测 ***

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值