机器学习之线性回归(纯python实现)

线性回归是机器学习中最基本的一个算法,大部分算法都是由基本的算法演变而来。本文着重用很简单的语言说一下线性回归。

线性回归

包括一元线性回归和多元线性回归,一元指的是只有一个x和一个y。通过一元对于线性回归有个基本的理解。

一元线性回归就是在数据中找到一条直线,以最小的误差来(Loss)来拟和数据。


image.png

上面提到的误差可以这样表示,假设那条直线如下图:


image.png

理想情况是所有点都落在直线上。退一步,希望所有点离直线的距离最近。简单起见,将距离求平方,误差可以表示为:


image.png

上面的i表示第i个数据。一般情况下对Loss求平均,来当作最终的损失。

最小化误差

找到最能拟合数据的直线,也就是最小化误差。

最小二乘法

上述公式只有m, b未知,因此可以看最一个m, b的二次方程,求Loss的问题就转变成了求极值问题。
这里不做详细说明。

另每个变量的偏导数为0, 求方程组的解。


image.png

image.png

求出m,b即可得到所要的直线。

梯度下降法

没有梯度下降就没有现在的深度学习。
最小二乘法可以一步到位,直接求出m,b。在大部分公式中是无法简单的直接计算的。而梯度下降通过一步一步的迭代,慢慢的去靠近那条最优的直线,因此需要不断的优化。
Loss的函数图像可以类比成一个碗。


image.png

要求的最小值就在碗底,随意给出一点往下走,即沿着下降最快的方向(梯度)往下走,定义每一步移动的步长,移动的次数来逼近最优值。
下面用算法来实现:

初始化:

def init_data():
    data = np.loadtxt('data.csv', delimiter=',')
    return data

def linear_regression():
    learning_rate = 0.01 #步长
    initial_b = 0
    initial_m = 0
    num_iter = 1000 #迭代次数

    data = init_data()
    [b, m] = optimizer(data, initial_b, initial_m, learning_rate, num_iter)
    plot_data(data,b,m)
    print(b, m)
    return b, m

优化器去做梯度下降:

def optimizer(data, initial_b, initial_m, learning_rate, num_iter):
    b = initial_b
    m = initial_m

    for i in range(num_iter):
        b, m = compute_gradient(b, m, data, learning_rate)
        # after = computer_error(b, m, data)
        if i % 100 == 0:
            print(i, computer_error(b, m, data)) # 损失函数,即误差
    return [b, m]

每次迭代计算梯度做参数更新:

def compute_gradient(b_cur, m_cur, data, learning_rate):
    b_gradient = 0
    m_gradient = 0

    N = float(len(data))
    #
    # 偏导数, 梯度
    for i in range(0, len(data)):
        x = data[i, 0]
        y = data[i, 1]

        b_gradient += -(2 / N) * (y - ((m_cur * x) + b_cur))
        m_gradient += -(2 / N) * x * (y - ((m_cur * x) + b_cur)) #偏导数

    new_b = b_cur - (learning_rate * b_gradient)
    new_m = m_cur - (learning_rate * m_gradient)
    return [new_b, new_m]

Loss值的计算:

def computer_error(b, m, data):
    totalError = 0
    x = data[:, 0]
    y = data[:, 1]
    totalError = (y - m * x - b) ** 2
    totalError = np.sum(totalError, axis=0)
    return totalError / len(data)

执行函数计算结果:

if __name__ == '__main__':
    linear_regression()

运算结果如下:

0 3.26543633854
100 1.41872132865
200 1.36529867423
300 1.34376973304
400 1.33509372632
500 1.33159735872
600 1.330188348
700 1.32962052693
800 1.32939169917
900 1.32929948325
1.23930380135 1.86724196887

可以看到,随着迭代次数的增加,Loss函数越来越逼近最小值,而m,b也越来越逼近最优解。

注意:

在上面的方法中,还是通过计算Loss的偏导数来最小化误差。上述方法在梯度已知的情况下,即肯定按照下降最快的方法到达碗底。那么在公式非常难以计算的情况下怎么去求最优解。此时求偏导数可以使用导数的定义,看另一个函数。

def optimizer_two(data, initial_b, initial_m, learning_rate, num_iter):
    b = initial_b
    m = initial_m

    while True:
        before = computer_error(b, m, data)
        b, m = compute_gradient(b, m, data, learning_rate)
        after = computer_error(b, m, data)
        if abs(after - before) < 0.0000001:  #不断减小精度
            break
    return [b, m]

def compute_gradient_two(b_cur, m_cur, data, learning_rate):
    b_gradient = 0
    m_gradient = 0

    N = float(len(data))

    delta = 0.0000001

    for i in range(len(data)):
        x = data[i, 0]
        y = data[i, 1]
        # 利用导数的定义来计算梯度
        b_gradient = (error(x, y, b_cur + delta, m_cur) - error(x, y, b_cur - delta, m_cur)) / (2*delta)
        m_gradient = (error(x, y, b_cur, m_cur + delta) - error(x, y, b_cur, m_cur - delta)) / (2*delta)

    b_gradient = b_gradient / N
    m_gradient = m_gradient / N
    #
    new_b = b_cur - (learning_rate * b_gradient)
    new_m = m_cur - (learning_rate * m_gradient)
    return [new_b, new_m]


def error(x, y, b, m):
    return (y - (m * x) - b) ** 2

上述两种中,迭代次数足够多都可以逼近最优解。
分别求得的最优解为:
1: 1.23930380135 1.86724196887
2: 1.24291450769 1.86676417482

简单比较

sklearn中有相应的方法求线性回归,其直接使用最小二乘法求最优解。简单实现以做个比较。

def scikit_learn():
    data = init_data()
    y = data[:, 1]
    x = data[:, 0]
    x = (x.reshape(-1, 1))
    linreg = LinearRegression()
    linreg.fit(x, y)
    print(linreg.coef_)
    print(linreg.intercept_)

if __name__ == '__main__':
    # linear_regression()
    scikit_learn()

此时求的解为:
1.24977978176 1.86585571。
可以说明上述计算结果比较满意,通过后期调整参数,可以达到比较好的效果。

源码和数据参考:
https://github.com/yunshuipiao/cheatsheets-ai-code

感谢并参考博文:
线性回归理解(附纯python实现)
Gradient Descent 梯度下降法
梯度下降(Gradient Descent)小结

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容