菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
455
0

线性回归

原创
05/13 14:22
阅读数 58692

 

 

线性回归

1. 介绍

a) 什么是线性回归

找到一条直线,使得这些点的距离到这条直线的距离尽可能的*,电当未知数据来的时候,就可以找到那个点,就是点的预测。 如果x的特征多,就是一个超*面,让我们的点尽可能的接*超*面.

b) 形式化定义:用数学来表示

(1) 假设函数

(2) 损失函数

预测值和真实值的差距

(3) 成本函数

每个点的损失值和 除以2是为了后面化解方便

2. 梯度下降法

a) 梯度下降法介绍

b) 梯度下降法数学表示

c) numpy代码实现梯度下降法

import numpy as np 
import matplotlib.pyplot as plt # 画图的包 
# 求y=x^2+2x+5的最小值 
# 函数图像 
X = np.linspace(-6, 4, 100) 
# 从-6到4之间取值,取100个值。 
Y = x**2 + 2*x + 5 Plt.plot(x, y) 
# 画出函数图像

# 初始化 x,a和迭代次数 
X=
# random函数,随便多少都行 
Alpha = 0.8 
# 步长 
IterateNum =10 
# 迭代次数 y的导数为2x+2,迭代次数theta 
For i in range(iterateNum):
    x = x - alpha*(2*x+2) 
    
# 最后输出x

参数设置的重要性 alpha参数 alpha决定参数往最值方向走的速度,如果参数设置的过小,那么x每次就只是走一点点,当数据很多很复杂的时候,下降的速度就会很慢,会经过很多次才会到达最低点; alpha给的过于大,就会一下子越过最低点,再下一次又跑到其他地方去了,就会在最低点的地方不断的震荡,无法收敛到最小值。 所以alpha不能过于大,也不能过于小。

iteraterNum迭代次数设置 到底迭代多少次它能达到最小值呢?有几种方法。

方法一: 上一次迭代的最小值下一次迭代和上一次迭代的值很相*了,不怎么变化了,收敛了。2 》1.96 〉1.9601》1.960001这时候可以去设置上一次的值和现在的值的差值,如果这个差值是在某一个很小的范围内,我们就认为这个迭代收敛了。

方法二: 花损失函数的图像,当两个损失值相差不多的时候,就可以结束迭代。

d) 梯度下降法求解线性回归问题

(1) 公式推导

希望代价函数越小越好,就要对它进行梯度下降。

(2) 例子

上面的迭代公式如何用呢?

(3) 使用梯度下降求解线性回归问题的代一码实现

i) 加载数据

# 数据集 6.1101,17.592  一行两条数据,分隔符是逗号,第二条是实际值  
Import numpy as np 
Import matplotlib.pyplot as plt 
# 定义一个加载数据的函数 
Def loaddata(): 
    data = np.loadtxt(‘data/data1.txt’, delimiter=‘,’) 
    n = data.shape[1] - 
 
# 特征数,有一条是实际值,所以减去1 
x = data[:, 0:n] # 所有行,从0开始的第n个特征。 
y = data[:, -1].reshape(-1, 1) # 所有行取最后一列。
return x, y # 这里的x还没有加入1的那列。

ii) 梯度下降法

def gradientDescent(x, y, iterations, alpha): 
    c = np.ones(x.shape[0]).transpose() # 全是1的列向量 
    x = np.insert(x, 0, values=c, axis=1) # 在x的0列上插入值c,对原始数据加入一个全为1的列 
    m = x.shape[0] # 数据量 
    n = x.shape[1] # 特征数    
    for num in range(iterations):   
        for j in range(n): # 对每个特征进行更新      
            theta[j] = theta[j]*(alpha/m)*(y - np.dot(x, theta)*x[:,j].reshape(-1, 1)))     
   return theta

iii) 当前情况下调用代码

# 调用它 
X, y = loaddata() 
Theta = np.zera(x.shape[1]+1).reshape(-1, 1) # 多加一列相当于b,theta0那一列。 
Iterations = 400 # 设定初始值
Alpha = 0.01 
Theta = gradDescent(x, y, theta, iterations, alpha) # 求出theta值 
# 画出图像 
Plt.scratter(x, y) 
# 散点图 
H_theta = theta[0]+theta[1]*x 
# 画出对应的直线
y = theta0*x0+theta1*x1 Plt.plot(x, h_theta)

iv) 特征归一化 特征数据的差值很大,不在一个量纲上,所以需要提前做一个归一化,比较简单,(x-均值)/方差。

Def featureNormalize(X): 
    mu = np.average(X, axis=0) # x第0列的均值 
    sigma = np.std(x, axis=0, doff=1) # doff是求方差的时候是除以n还是n-1. 
    x = (x-mu)/sigma # 归一化 
    return x, mu, sigma # 再调用上一个块的函数。

v) 画损失函数图

画出这个图像对我们观察大概迭代到哪一次它的迭代基本上就不会降低了,对于我们观察算法是很好的方式。

# 损失函数 
Def computeCost(x, y, theta): 
    m = x.shape[0] np.sum(np.power(np.dot(x, theta) - y, 2))/(2*m))

# 更新前面的代码。 
def gradientDescent(x, y, iterations, alpha): 
    c = np.ones(x.shape[0]).transpose() # 全是1的列向量 
    x = np.insert(x, 0, values=c, axis=1) # 在x的0列上插入值c,对原始数据加入一个全为1的列 
    m = x.shape[0] # 数据量 
    n = x.shape[1] # 特征数 
    costs = np.zeros(iterations)    
    for num in range(iterations):   
        for j in range(n): # 对每个特征进行更新      
            theta[j] = theta[j]*(alpha/m)*(y - np.dot(x, theta)*x[:,j].reshape(-1, 1))/)    
            costs[num] = computerCost(x, y, theta)  
    return theta, costs
# 接着调用运行的代码。 
# 画损失函数值 
X_axis = np.linspace(1, iterations, iterations) # x轴,从1开始到迭代次数,取迭代次数那么多个 
Plt.plot(x_axis, costs[0:iterations]) # 每次迭代取出cost值。

vi) 预测函数

Def predict(x): 
    x = (x-mu)/sigma # 依然要归一化 
    c = np.ones(x.shape[0]).transpose() 
    x = np.insert(x, 0, value=c, axis=1) # 加一层1的列向量 
    return np.dot(x, theta) # 代入公式

(4) 梯度下降法的优化(变形)

i) 批量梯度下降法

要用到x里面的所有数据依次更新,在theta0,theta1更新的时候,我们要用上所有的数据,每个theta更新我们都要用上所有数据,如果数据量不大其实也还好,但是如果数据量上百万千万的时候,做一次更新是非常耗时的。所以提出随机梯度下降。

ii) 随机梯度下降法

与批量梯度下降的区别就是没有求和了,我们的每一个参数更新只选择一个样本就可以了。比如我们第一次拿出来的是样本1,那么我们就可以更新每个theta了,下一次可以拿出来样本2.可以加快随机梯度下降的速度,减少计算量。 有个问题,每次都是拿一个样本,这个样本下降的方向不是我们减少最快的方向,就是会震荡下降,如果用所有样本相当于全局样本都看了一遍,相对来说下降方向是比较准确的。所以提出小批量梯度下降。

iii) 小批量梯度下降法

它是介于批量梯度下降和随机梯度下降的折中。我们每次只取一部分。我们先把x的样本顺序打乱,比如batch—size取2,这次取2条进行一次梯度下降,下次再取2条进行梯度下降。综合前两个的优点即选择一批梯度下降,样本个数又不会太多,所以在实际应用中,尤其是数据量比较大的时候,推荐使用小批量梯度算法。

3. 模型评价指标

a) 均方误差(MSE)

Y_true = np.array([1, 2, 3, 4, 5]) 
Y_pred = np.array([1.1, 2.2, 3.1, 4.2 ,5]) 
Def mse(y_true, y_pred): 
    return np.sum(np.power(y_true-y_pred, 2))/len(y_true)

b) 均方根误差(RMSE)

def rmse(y_true, y_pred): 
    return np.sqrt(np.sum(np.power(y_true-y_pred, 2))/len(y_true))

c) *均绝对误差(MAE)

def Mae(y_true, y_pred): 
    return np.sum(np.abs(y_true-y_pred))/len(y_true)

4. 欠拟合与过拟合的概念

a) 欠拟合与过拟合

欠拟合:训练出来的结果没有很好的拟合数据。

过拟合:虽然每个点都穿过了,但是不能预测未来的数据,它的泛化能力差,过分的拟合了训练数据,但是无法很好的对我们的预测数据进行预测。

适合的拟合:不是所有的点都穿过,基本上符合数据的走势,当我们对预测数据进行预测的时候,这条数据就能够很好的表示这样的测试数据。

所以希望训练出来的模型是对训练数据拟合的不错,同时对测试数据预测也不错的一条曲线。

b) 正则化

为了解决过拟合的风险,提出了几种正则化的方式。

(1) 岭回归

在原来的代价函数基础上加入一个参数的二范式,对参数加入一个惩罚因子lamda,这个项是正则化项。 加入了二范式的惩罚因子以后,我们也把它叫做L2正则化,使用了L2正则化的线性回归叫做岭回归。

i) 岭回归求解

def gradientDescent_ridge(x, y, iterations, alpha, lamda=0.02): 
    c = np.ones(x.shape[0]).transpose() # 全是1的列向量 
    x = np.insert(x, 0, values=c, axis=1) # 在x的0列上插入值c,对原始数据加入一个全为1的列 
    m = x.shape[0] # 数据量 n = x.shape[1] # 特征数   
    for num in range(iterations):   
        for j in range(n): # 对每个特征进行更新      
            theta[j] = theta[j]*(alpha/m)*(y - np.dot(x, theta)*x[:,j].reshape(-1, 1)))-2*lamda*theta[j]    
    return theta # 只改变了蓝色部分。

ii) 为什么使用岭回归可以减小过拟合

H0(x) = theta0 + theta1*x1 + theta2*x2 + theta3*x3 1+0.1*x1 + 0.0001*x2 + 0.6*x3

Theta2的数值非常的小,就是说给定x1,x2,x3这些训练数据的时候,x2对于函数值的影响比较小。一个很小的数乘以一个数,相对来说它的结果会比较小,那么这个结果加上去对这个函数的影响就会比较小。相当于只有x1,x3起作用,三个特征只有两个特征起作用,这个模型相对来说就比较简单,模型越简单,过拟合风险就越小,越复杂过拟合风险就越大。模型越复杂拟合程度就会越好。既然想减少过拟合风险,就要想办法使得模型简单,就是想办法让某一个theta值变得很小,theta小的时候特征就不再起作用,所以通过训练 数据去决定哪个theta比较小。 看thetaj最后的推导结果,提出thetaj,(1-2lamda)thetaj,假设lamda是0~1,1-2lamda就是0~1,乘以thetaj肯定比原来的thetaj要小,中间的项可以看作一个定值,所以新的thetaj是更小的,5》4.7〉4.5 减小的程度由训练数据决定的,比如有的减小速度很快,theta2,那么它就不重要。

(2) LASSO回归

使用了L1范式的线性回归我们把它叫做LASSO回归。

i) 求解

ii) 举例

iii) 代码实现LASSO回归

Import numpy as np 
Import matplotlib.pyplot as plt 
# 定义一个加载数据的函数 
Def loaddata(): 
    data = np.loadtxt(‘data/data1.txt’, delimiter=‘,’) 
    n = data.shape[1] - # 特征数 
    x = data[:, 0:n] 
    y = data[:, -1].reshape(-1, 1) 
    return x, y 
# 特征归一化 归一化方法有很多,这里是: 对每个特征,这列中的每个数据分别减去这列的均值,再除以这列的方差。 
Def featureNormalize(x):
    mu = np.average(x, axis=0) 
    sigma = np.std(x, axis=0, Dodd=1) 
    x = (x - mu)/sigma 
    return x, mu, sigma 
# LASSO回归的核心代码 
Def lasso_regression(x, y, iterations, lambd=0.2): 
    m, n = x.shape # 数据的行数和特征数 
    theta = np.matrix(np.zeros((n, 1))) # 参数,跟x的特征数一致 
    for it in range(iterations):    
        for k in range(n): # n个特征       
            # 计算常量值z_k和p_k      
            z_k = np.sum(x[:, k]**2) 
            # 当前数据的第k个特征        
            p_k = 0         
            for i in range(m):          
                p_k += x[i, k]*(y[i, 0]-np.sum([x[i,j]*theta[j, 0] for j in range(n) if j != k]))       
                # z_k是个临时变量,根据p_k的不同取值进行计算      
                if p_k < -lamda/2:      
                    w_k = (p_k+lamda/2)/z_k     
                elif p_k > lamda/2:         
                    w_k = (p_k-lamda/2)/z_k     
                else:       
                    w_k = 0         
                theta[k, 0] = w_k 
    return theta 
# 调用代码 
X,y = loaddata() 
X, mu, sigma = featureNormalize(x) # 数据归一化 
x_1 = np.insert(x, 0, values=1, axis=1) # 插入一列值为1的数据。 
Theta = lasso_regression(x_1, y, 100) 
Print(theta) 
plt.scatter(x, y) 
Line = theta[0, 0] + theta[1, 0]*x 
Plt.plot(x, line)

(3) 弹性网

综合L1与L2解决过拟合,我们把它叫做弹性网。rou是L1和L2的比例。

5. 使用Sklearn实现线性回归

a) 最小二乘法求解线性回归

b) 最小二乘法的代码实现

Import numpy as np 
Import matplotlib.pyplot as plt 
Def loaddata(): 
    data = np.loadtxt(‘data/data1.txt’, delimiter=‘,’) 
    n = data.shape[1]- 
    x = data[:, 0:n] 
    y = data[:, -1].reshape(-1, 1) 
    return x, y 
X_origin, y = loaddata() 
X = np.insert(x_origin, 0, values=1,axis=1) # 加入一列全是1的特征 求解theta 
Theta = np.Linalg.inv(x.T.dot(x)).dot(x.T).dot(y) 
"""
(1)当数据量比较大的时候,求矩阵的逆其实也蛮慢的,所以数据量比较大的时候就还是用梯度下降法迭代。 
(2)如果矩阵的逆不存在,那么求解逆就会出错,可以改写括弧里面加一个lamda*I(对角线都是1),保证逆存在。
"""
# 画图 
Plt.scatter(x_origin, y) 
h_theta = theta[0] + theta[1]*x_origin 
Plt.plot(x_origin, h_theta)

c) 使用Sklearn实现Ridge,LASSO和ElasticNet

(1) 加载数据

Import numpy as np 
Import matplotlib.pyplot as plt 
From sklearn import linear_model 
Def loaddata(): 
    data = np.loadtxt(‘data/data1.txt’, delimiter=‘,’) 
    n = data.shape[1] - 
    x = data[:, 0:n] y = data[:, -1].reshape(-1, 1) 
    return x, y 
X, y = loaddata()

(2) 画图

Plt.scatter(x, y) 
Y_hat = model1.predict(x) 
Plt.plot(x, y_hat)

(3) 最小二乘

Model1 = linear_model.LinearRegression() 
Model1.fit(x, y) Print(model1.coef_) # 输出系数,theta_1到theta_n 
Print(model1.intercept_) # 输出截距,输出theta_0

(4) Ridge

Model2 = linear_model.Ridge(alpha=0.01,normalize=True) # 这里的alpha相当于课堂上的lamda,表示正则化强度。 
Model2.fit(x, y) 
Print(model2.coef_) # 输出系数,theta_1到theta_n 
Print(model2.intercept_) # 输出截距,输出theta_0

(5) LASSO

Model3 = linear_model.Lasso(alpha=0.01,normalize=True) # 这里的alpha相当于课堂上的lamda,表示正则化强度。 
Model3.fit(x, y) Print(model3.coef_) # 输出系数,theta_1到theta_n 
Print(model3.intercept_) # 输出截距,输出theta_0

(6) ElasticNet

Model2 = linear_model.ElasticNet(alpha=0.01,normalize=True) # 这里的alpha相当于课堂上的lamda,表示正则化强度。 
Model2.fit(x, y) Print(model2.coef_) # 输出系数,theta_1到theta_n 
Print(model2.intercept_) # 输出截距,输出theta_0

6. 案例: 波士顿房价预测

a) 机器学习项目流程

1、获取数据

无论是自己收集,还是公开等等。

2、数据预处理(数据清洗)

数据不干净,有缺失值不全,数据有错误的值,有重复的数据。

3、数据分析和可视化

人为的分析数据,可视化,抽出几个维度看看什么关系,对数据有一些了解。

4、选择适合的机器学习模型

根据数据的样子选择一学习机器学习模型,看看什么模型适合,什么不适合,多用几个看看。

5、训练模型(使用交叉验证选择合适的参数)

模型里面有很多参数,用交叉验证选择合适的参数。

6、评价模型

看看它在测试集上面的表现如何,如果在测试集上表现不错,就可以进入到上线部署。

7、上线部署使用模型

b) 代码

(1) 引入包

Import numpy as np 
Import matplotlib.pyplot as plt 
From sklearn import linear_model 
From sklearn.datasets import load_boston # 直接加载波士顿数据 
From sklearn.model_selection import train_test_split 
From sklearn.metrics import mean_squared_error From sklearn.metrics 
import mean_absolute_error

(2) 获取数据

# 1、自帶的數據集 
Boston = load_boston() 
"""特征含义 
CRIM: 城镇人均犯罪率 
ZN: 住宅用地超过 250000 的比例 
INDUS:城镇非零售土地的比例 
CHAS:查理斯河空变量(如果边界是河流,则1,否则0) 
NOX:一氧化碳浓度 
RM:住宅*均房间数 
AGE:1940年之前建成自用房屋比例 
DIS:到波士顿五个中心区域的加权距离 
RAD:辐射性公里的接*指数 
TAX:每10000美元的全值财产税率 
PTRATIO:城镇师生比例 B:1000^2,其中Bk指代城镇中黑人比例 
LSTAT:人口中地位低下者的比例 
MEDV:自住房的*均房价,以千美元计 
"""
Boston.DESCR # 对数据集的描述
# 2、从文件中读取数据集 
Import pandas as pd 
Df = pa.read_excel(‘data/boston.xls’) 
X = df[df.columns[0:-1]] 
y = df[df.columns[-1]]

(3) 选择适合的机器学习模型

model1 =linear_model.Ridge(alpha=0.1) 
Model1.fit(x, y) 
Y_hat = model.predict(x)

代码

X_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2) # 分割数据
# 交叉验证 
From sklearn.model_selection import GridSearchCV 
Ridge_model = {‘alpha’: [0.01, 0.03, 0.05, 0.07, 0.1, 0.5, 0.8, 1], ‘normalize’: [True,Fasle]} 
# ridge模型里面需要调节的参数 
Gsearch = GridSearchCV( 
    estimator=ridge_model, # 什么模型 
    param_grid=param, # 调节的参数,然后就会对每个参数值一一匹配,找出最大值那个 
    cv=5, # 5折交叉验证 
    scoring=‘neg_mean_squared_error’) # 采取什么评分,均方误差,只不过前面加了一个负号 
                       
# 然后查看训练出来的参数 
Gsearch.best_params_, gsearch.best_score_

(4) 模型评价和上线部署

5、模型评价

Final_model = linear_model.Ridge(alpha=0.01, normalize=True) 
Final_model.fit(x_train, y_train) 
Y_test_hat = Final_model.predict(x_test) 
Print(“test MSE”, mean_squared_error(y_test, y_test_hat))

实际上GridSearchCV会运行好多次的,这次0。03效果好,舅子0.03附*多取几个数。

6、上线部署

6、1模型保存

 from sklaern.externals import joblib 
 Joblib.dump(final_model, “house_train_model.m”)

6、2模型读取

Load_model = joblib.load(“house_train_model.m”)

发表评论

0/200
455 点赞
0 评论
收藏