前言
之前有大致了解过机器学习相关的知识,最近业余时间在网易云课堂上正式看了NG大大的 机器学习,在收获之余也将学习内容沉淀并赋予实践。
本文就是我学习到的第一个算法梯度下降算法。
梯度下降
梯度下降法(英语:Gradient descent)是一个一阶最优化算法,通常也称为最速下降法。要使用梯度下降法找到一个函数的局部极小值,必须向函数上当前点对应梯度(或者是近似梯度)的反方向的规定步长距离点进行迭代搜索。
这种可以用来计算一个存在的局部最小值--所能够解释到的就是对一个线性回归函数的参数预测。
线性回归
在统计学中,线性回归(英语:linear regression)是利用称为线性回归方程的最小二乘函数对一个或多个自变量和因变量之间关系进行建模的一种回归分析。这种函数是一个或多个称为回归系数的模型参数的线性组合。只有一个自变量的情况称为简单回归,大于一个自变量情况的叫做多元回归(multivariate linear regression)
线性回归可以用于预测房价趋势,预测疾病发病率,预测消费支出这种,本文的实例就是预测一个房价数据集中房间面积和售价之间的关系。
基本概念
就单对一组房价数据将,简单地理解为价格和房间面积的关系是线性变化的(假定),那么就存一个预测函数H:
Hypothesis 就是我们对这个房价预测的一个线性函数,变量x就是房间面积,h就是我们预测的房价而其中的 Parameters \theta0 \theta1 就是我们需要通过预测计算出来的参数,为了计算出 这个两个预测的参数我们需要将预测的函数通过与y值比较求方差,校验预测结果与真实结果的差距--代价函数(Cost Function)。
同时我们要让代价函数的代价最小 -- 即是让预测函数无限逼近于真实的数据,就是让代价函数的值最小,这样说明预测函数与真实的y 房价的值数据越接近,所以需要一个算法去计算出代价函数的最小值--这样就引出了一个算法 梯度下降(Gradient descent).
在使用梯度下降算法之前我们要知道一个点,就是如何求一个函数的最小值,简单地说就是通过对函数求导(求斜率),当斜率为零或无限接近0的时候,这个点就是它的局部最小值(可能存在多个斜率为0的函数),那么梯度下降算法要做的就是计算出这个最小值的点。
下图是每个参数的算法公式,其中 \alpha 是学习率,用于计算下降的速率
求偏导后,其中 X0 = 1(讲义里定义为 截距项 intercept term), m 为训练数据的数量
按讲义上的公式,我们只要重复计算,直到代价函数的偏导接近于一个很小的值就能够得到我们预测的参数 \thetaj
实践
1.数据集
本次使用的是kaggle上的数据集,是一个叫做 House Sales in King County, USA的数据集,是美国一个城市 2014年5月~2015年5月房屋售价的数据集合。
2.数据分析
下载数据后用py查看
import pandas as pd
df = pd.read_csv("input/kc_house_data.csv", )
print(df.shape)
有21613条数据,通过数据集来源可以看到字段 price售价, sqft_living 是房间面积,那么看下散点图:
import matplotlib.pyplot as plt
df = df[['price', 'sqft_living']]
#1.绘制价格和房间规格大小的点图
alt = array(df)
x = []
y = []
for a in alt:
x.append(a[1])
y.append(a[0])
x, y = array(x),array(y)
plt.figure()
plt.scatter(x, y, s=5, c='red', label = 'Predicted Regression Line')
plt.xlabel('Living Space (sqft)')
plt.ylabel('Price ($)')
plt.show()
3.Coding
需要预定义一些数据:两个参数的初始值,学习率,代价函数的极限值,数量m
# 定义预测函数 h = z + z1 * x 预测为线性
# 定义学习率 a
a = 0.01
# 定义初始值
z = 0
z1 = 50
# 数量长度
# 因数值太大 先缩小
x = x / 1000
y = y / 1000
m = len(x)
# 定义极限
min_limit = 1e-3
定义函数,获取 h:
def getH(z, z1, x, y):
return z + z1 * x - y
定义获取 z 偏导结果:
def getZ(z, z1, x, y, m):
sum = 0
for i in range(m):
h = getH(z, z1, x[i], y[i])
sum = sum + h
sum = sum * (1./ m)
# newZ = z - sum
return sum
定义 获取 z1 偏导结果:
def getZ1(z, z1, x, y, m):
sum = 0
for i in range(m):
h = getH(z, z1, x[i], y[i])
h = h * x[i]
sum = sum + h
sum = sum * (1./ m)
# newZ1 = z1 - sum
return sum
获取结果 all Z:
def getAllZ(z, z1, x, y, m, a, min_limit):
newZ = getZ(z, z1, x, y, m)
newZ1 = getZ1(z, z1, x, y, m)
#梯度下降操作 如果sum 结果比极限值大 则还未接近0,继续梯度下降
while abs(newZ) >= min_limit and abs(newZ1) >= min_limit:
print('z z1', z, z1)
print('new z new z1', newZ, newZ1)
z = z - a * newZ
z1 = z1 - a * newZ1
newZ = getZ(z, z1, x, y, m)
newZ1 = getZ1(z, z1, x, y, m)
print('new z', newZ)
print('new z1', newZ1)
print('z z1', z, z1)
return [z, z1]
最后调用方法获取结果:
result = getAllZ(z, z1, x, y, m, a, min_limit)
#10条数据 z = -13.80519903101423 z1 = 225.65934680567906
#全部数据 z = -43.56346904629207 280.61642450710656
def testY(x, result):
return (result[0] + result[1] * x);
x = x * 1000
y = y * 1000
text_y = []
for i in x:
# print(i)
text_y.append(testY(i, result))
plt.figure()
plt.scatter(x, y, s=5, c='red', label='Data')
plt.plot(x, text_y, c='darkgreen', label='Predicted Regression Line')
plt.xlabel('Living Space (sqft)')
plt.ylabel('Price ($)')
plt.xticks(fontsize=13)
plt.yticks(fontsize=13)
plt.legend()
plt.show()
输出绘制的结果图 :
10条数据的时候:
全部数据(单位/1000):
最后得到预测函数,
h = -43.56346904629207 + 280.61642450710656 * x
至此预测房价与房间面积的线性回归预测已经全部结束,也是成功记录了本人在学习机器学习原理的第一步!
4.矩阵形式
2019.10.10更新
矩阵的基本公式就不放出来了,其实就是其原理的拓展,已知有一下求解预测函数参数的方法:
代价函数为:
其中的推论就不贴出来了,cs229 有相关推论 求偏导后:
thea - a * x.T * (x * thea - y) * 1./ m
coding 下:
#梯度下降矩阵型式
import os
import sys
import math
import pandas as pd
import matplotlib.pyplot as plt
import numpy.matlib
import numpy as np
#导入数据
df = pd.read_csv("../20190820/input/kc_house_data.csv")
#因是简单的一个线性回归 参数先使用2个 price 和 sqft_living
#需要以下矩阵 价格结果矩阵 面积矩阵
y = df[['price']]
x = df[['sqft_living']]
m = len(x)
a = 0.01 #学习率
#将df 转换成矩阵
y = np.asmatrix(y) / 1000
x = np.asmatrix(x) / 1000
#因x0 默认为1 需要一个 1xm的 x0矩阵
x0 = np.matlib.ones((m, 1))
#合并矩阵 2 x m
x = np.concatenate([x0, x], 1)
#定义thea 目前设置为2个变量 默认为0
thea = np.matlib.zeros((2, 1))
diff = np.dot(x, thea) - y
diff = np.dot(np.transpose(x), diff) * (1./m)
while not np.all(np.absolute(diff) <= 1e-5):
#loop
thea = thea - a * diff
diff = np.dot(x, thea) - y
diff = np.dot(np.transpose(x), diff) * (1./m)
print('thea: ', thea)
print('final thea', thea)
当 J 的偏导值(绝对值)很小,本文中是1e-5,就可以认为已经到达局部极小值点。 跑完结果输出:
结果和上面纯代码型式是一样的,不过矩阵形式代码简洁,少写很多循环代码,对于这种需要大量计算的算法很减少很多的事情。
以上。 相关源码地址: Gayhub