机器学习一,使用numpy进行简单的机器学习

使用numpy进行简单的机器学习

前言

大多数机器学习的例子都是都是关注视觉和自然语言方向,本人尝试就阅读过的文章整理一系列关于数据预测,时序等的例子,来记录学习机器学习的过程。

机器学习并不是tensorflow等的专利,对于简单的模型,使用一些常规的计算库也可以求解,本文先使用numpy 进行手动的梯度下降,来完成机器学习。

题目

考虑一个实际问题,某城市在 2013 年 - 2017 年的房价如下表所示:

年份201320142015201620172018
房价120001400015000165001750018400

现在,我们希望通过对该数据进行线性回归,即使用线性模型 y = ax + b 来拟合上述数据,此处 a 和 b 是待求的参数。

环境

开发环境比较简单,安装 最新 anaconda 即可, 我安装的版本默认内置 python 3.8。 使用Spyder 执行代码, spyder默认不能跑plt 图像界面,前往 修改设置即可。

分析

首先,我们定义数据,进行基本的归一化操作。

import numpy as np

X_raw = np.array([2013, 2014, 2015, 2016, 2017,2018], dtype=np.float32)
y_raw = np.array([12000, 14000, 15000, 16500, 17500, 18400], dtype=np.float32)

X = (X_raw - X_raw.min()) / (X_raw.max() - X_raw.min())
y = (y_raw - y_raw.min()) / (y_raw.max() - y_raw.min())

接下来,我们使用梯度下降方法来求线性模型中两个参数 a 和 b 的值 。过程如下:

  • 初始化为 a=b=0
  • 计算 y_pred = a * X + b, 通过 y_pred 计算 a,b的梯度。grad_a, grad_b = 2 * (y_pred - y).dot(X), 2 * (y_pred - y).sum()
  • 按 指定的学习速率 learning_rate , grad_a, grad_b , 计算 新的 a,b ,循环计算。
  • 循环指定次数或者达到 目标cost 停止计算,梯度下降结束。

为方便展示,使用plt模块增加了推导进度的显示,可以看到 我们从一个 横线 y=0*x+0, 推导得到 最终的结果。

完整源码:

"""
    使用 numpy进行梯度下降的回归训练
    X, y 定义了屏幕上的几个点,  找出理想的 y=ax+b来 穿过这些点,差异最小。
    默认 a, b = 0, 0 
    训练的过程中使用 plt 来 展现 X, y 的值 以及 a, b 的变化。 看到 y=ax+b 逐渐贴近 所有的 X,y。
    
Created on Sat Feb 20 21:30:29 2021
@author: huwp001
    
"""

import numpy as np
import matplotlib.pyplot as plt

"""
原始数据 X_raw, y_raw
"""
X_raw = np.array([2013, 2014, 2015, 2016, 2017, 2018], dtype=np.float32)
y_raw = np.array([12000, 14000, 15000, 16500, 17500, 18400], dtype=np.float32)

"""
进行归一化,转换为了 0,1 之间的值
"""
X = (X_raw - X_raw.min()) / (X_raw.max() - X_raw.min())
y = (y_raw - y_raw.min()) / (y_raw.max() - y_raw.min())

"""
默认从横线开始训练 y=ax+b
"""
a, b = 0, 0

num_epoch = 10000
learning_rate = 5e-4
for e in range(num_epoch):
    # 手动计算损失函数关于自变量(模型参数)的梯度
    y_pred = a * X + b
    
    grad_a, grad_b = 2 * (y_pred - y).dot(X), 2 * (y_pred - y).sum()

    # 更新参数
    a, b = a - learning_rate * grad_a, b - learning_rate * grad_b
    
    if e % 500 == 0 or e==num_epoch-1:
        # plot and show learning process
        plt.cla()
        plt.scatter(X, y)
        plt.plot(X, y_pred, 'r-', lw=5)
        plt.text(0.5, 0, 'Loss=%.4f' % grad_a, fontdict={'size': 20, 'color': 'red'})
        plt.text(0.5, 0.12, 'y=%.4f*x + %.4f' % (a, b), fontdict={'size': 20, 'color': 'red'})
        plt.pause(0.1)

plt.ioff()
plt.show()
print(a, b)

运行

执行程序开始推导,红线是初始 y=ax+b, 且 a=b=0
开始推导,红线是初始 y=ax+b
梯度下降执行一段时间后,横线开始接近真实数据。
推导一段时间后的图像,可见横线开始接近真实数据
循环10000次后结果,红线比较完美的穿过数据点。
红线近乎完美的穿过数据点

总结

梯度下降的主要逻辑是 初始化变量,计算损失Loss,计算梯度Grad,用梯度更新变量,继续循环。由于原始数据接近一元一次函数,我们使用的Loss函数,梯度函数都是针对一元一次函数手工编写的。如:grad_a, grad_b = 2 * (y_pred - y).dot(X), 2 * (y_pred - y).sum() 。 修改该代码直接影响训练的效果。手工求函数关于参数的偏导数。如果是简单的函数或许还好,但一旦函数的形式变得复杂(尤其是深度学习模型),手工求导的过程将变得非常痛苦,甚至不可行。

另外,经常需要手工根据求导的结果更新参数。这里使用了最基础的梯度下降方法,因此参数的更新还较为容易。但如果使用更加复杂的参数更新方法(例如 Adam 或者 Adagrad),这个更新过程的编写同样会非常繁杂。

而 TensorFlow 等深度学习框架的出现很大程度上解决了这些痛点,为机器学习模型的实现带来了很大的便利。

参考文章:

简单粗暴 tensorflow2 前往

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值