赞
踩
在之前的博客当中描述了怎样模拟出了梯度下降的过程
如果是多维情况,theta其实是一个向量,那么对其求导的损失函数也是向量,梯度就是损失函数对每个方向的theta求偏导。
和之前的一维线性回归相比,我们对只是对w这个数字进行求导,而现在针对多系数theta,对theta整个向量进行求导。
有两个参数的梯度下降法进行可视化,一圈一圈代表等高线,圈上的值就是梯度。越外层J的取值越大,越里层J的取值越小,在图的中心位置达到损失函数的最小值
对每一个维度的theta进行求导(这里的Xb注意依然是加上了一列恒为1的一列,代表截距)
在上面的式子当中,可以看到求导之后的结果其实是一个求和的过程,也就是说在这里如果样本数量越大,梯度也就越大,这显然是不合理的,最后我们的梯度应该和样本数量m是无关的。所以下面改变目标函数(除以样本数量m,再提取公共的2),最后发现这个结果就是y的预测值和真值之间的MSE值(均方误差)
这里为了可视化方便,采取的数据都是一维的数据,但是实际上是对矩阵进行处理。
(1)损失函数
np.sum((y - X_b.dot(theta)) ** 2 / len(X_b)
(2)求梯度
# 梯度(比较笨的方法)
def dJ(theta, X_b, y):
res = np.empty(len(theta)) # 开一个和theta一样大的空间(因为要对theta的每一个元素求偏导)
res[0] = np.sum(X_b.dot(theta) - y) # 第一行单独写,其他的要写一个循环
for i in range(1, len(theta)):
res[i] = (X_b.dot(theta) - y).dot(X_b[:,i]) # X_b[:,i]相当于取出第i列,也就是第i个特征值
return res * 2 / len(X_b)
(3)X_b
# X添加一列样本个数行、1列,每个取值都是1的一列
X_b = np.hstack([np.ones((len(X), 1)), X])
(4)总体代码
# 多维线性回归问题(梯度下降法) import numpy as np import matplotlib.pyplot as plt # 设置随机种子 np.random.seed(666) # 用一维数据方便可视化,但实际是对矩阵进行操作 x = np.random.random(size = 100) y = x * 3. + 4. + np.random.normal(size = 100) # 噪音:均值为0,方差为1 X = x.reshape(-1, 1) # 数组array中的方法,作用是将数据重新组织成100行1列的数据 # print('X数组', X.shape) # print('y数组', y.shape) plt.scatter(x, y) plt.show() # 损失函数 def J(theta, X_b, y): try: return np.sum((y - X_b.dot(theta)) ** 2 / len(X_b)) except: return float('inf') # 返回float最大值 # 梯度(比较笨的方法) def dJ(theta, X_b, y): res = np.empty(len(theta)) # 开一个和theta一样大的空间(因为要对theta的每一个元素求偏导) res[0] = np.sum(X_b.dot(theta) - y) # 第一行单独写,其他的要写一个循环 for i in range(1, len(theta)): res[i] = (X_b.dot(theta) - y).dot(X_b[:,i]) # X_b[:,i]相当于取出第i列,也就是第i个特征值 return res * 2 / len(X_b) # 直接复制之前模拟梯度下降过程的代码,然后进行更改 def gradient_descent(X_b, y, initial_theta, eta, n_iters = 1e4, epsilon = 1e-8): theta = initial_theta i_iters = 0 while i_iters < n_iters: gradient = dJ(theta, X_b, y) # 求梯度 last_theta = theta # theta重新赋值前,记录上一场的值 theta = theta - eta * gradient # 通过一定的eta学习率取得下一个点的theta # 最近两点的损失函数差值小于一定精度,退出循环 if(abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon): break i_iters += 1 return theta ''' 初始化数值 ''' # X添加一列样本个数行、1列,每个取值都是1的一列 X_b = np.hstack([np.ones((len(X), 1)), X]) initial_theta = np.zeros(X_b.shape[1]) # 设置n+1维的向量,X_b.shape[1]:第一行的维数 eta = 0.1 theta = gradient_descent(X_b, y, initial_theta, eta) print('theta', theta)
运行结果
这里的theta[0]就是截距,theta[1]就是斜率,和我们一开始设置的一致,说明梯度下降法成功训练了我们的模型
y = x * 3. + 4. + np.random.normal(size = 100)
所有的思想都在上面,最后我把多元线性回归的两种方法总结在了一起
# 多元线性回归问题;解析解+梯度下降法(也能解决一元线性回归问题) import numpy as np from sklearn import datasets from sklearn.metrics import r2_score from sklearn.model_selection import train_test_split class LinearRegression: def __init__(self): # 初始化Linear Regression模型 self.coef_ = None # 系数,对应theta1-n,对应的向量 self.interception_ = None # 截距,对应theta0 self._theta = None # 定义私有变量,整体计算的theta ''' 梯度下降 ''' def fit_gd(self, X_train, y_train, eta = 0.01, n_iters = 1e4): # 根据训练数据集X_train, y_ .train训练Linear Regression模型 # X_train的样本数量和y_train的标记数量应该是一致的 # 使用shape[0]读取矩阵第一维度的长度,在这里就是列数 assert X_train.shape[0] == y_train.shape[0], \ "the size of x_ .train must be equal to the size of y_ train" # 损失函数 def J(theta, X_b, y): try: return np.sum((y - X_b.dot(theta)) ** 2 / len(y)) except: return float('inf') # 返回float最大值 # 梯度(比较笨的方法) def dJ(theta, X_b, y): res = np.empty(len(theta)) # 开一个和theta一样大的空间(因为要对theta的每一个元素求偏导) res[0] = np.sum(X_b.dot(theta) - y) # 第一行单独写,其他的要写一个循环 for i in range(1, len(theta)): res[i] = (X_b.dot(theta) - y).dot(X_b[:,i]) # X_b[:,i]相当于取出第i列,也就是第i个特征值 return res * 2 / len(X_b) # 求解theta矩阵 def gradient_descent(X_b, y, initial_theta, eta, n_iters = 1e4, epsilon = 1e-8): theta = initial_theta cur_iters = 0 while cur_iters < n_iters: gradient = dJ(theta, X_b, y) # 求梯度 last_theta = theta # theta重新赋值前,记录上一场的值 theta = theta - eta * gradient # 通过一定的eta学习率取得下一个点的theta # 最近两点的损失函数差值小于一定精度,退出循环 if(abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon): break cur_iters += 1 return theta # 得到X_b X_b = np.hstack([np.ones((len(X_train), 1)), X_train]) initial_theta = np.zeros(X_b.shape[1]) # 设置n+1维的向量,X_b.shape[1]:第一行的维数 # X_b.T是X_b的转置,.dot是点乘,np.linalg.inv是求逆 # 获取theta self._theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters) self.interception_ = self._theta[0] # 截距 self.coef_ = self._theta[1:] # 系数 return self ''' 解析解 ''' # 正规化方程 # 训练过程(解析解) def fit_normal(self, X_train, y_train): # X_train和y_train都是矩阵 # 根据训练数据集X_train, y_ .train训练Linear Regression模型 # X_train的样本数量和y_train的标记数量应该是一致的 # 使用shape[0]读取矩阵第一维度的长度,在这里就是列数 assert X_train.shape[0] == y_train.shape[0], \ "the size of x_ .train must be equal to the size of y_ train" # np.hstack():在水平方向上平铺,就是在横向上多加一列 # np.ones(矩阵大小, 列数)是增加一列恒为1的一列 # 得到X_b X_b = np.hstack([np.ones((len(X_train), 1)), X_train]) # X_b.T是X_b的转置,.dot是点乘,np.linalg.inv是求逆 self._theta = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y_train) self.interception_ = self._theta[0] # 截距 self.coef_ = self._theta[1:] # 系数 return self ''' 预测过程 ''' def predict(self, X_predict): # 给定待预测数据集X_predict,返回表示X_predict的结果向量 X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict]) return X_b.dot(self._theta) ''' 确定精度,评价多元线性回归的结果 ''' def score(self, X_test, y_test): # 根据测试数据集X_test 和y_test 确定当前模型的准确度 y_predict = self.predict(X_test) return r2_score(y_test, y_predict) # r2_score求真值y_test和预测值y_predict的r方 ''' 显示属性 ''' def __repr__(self): return "LinearRegression()" # 加载波士顿房价数据集,并划分为X_train,y_train # 波士顿房价 boston = datasets.load_boston() # X_train = boston.data # y_train = boston.target X = boston.data y = boston.target X = X[y < 50.0] y = y[y < 50.0] # 分出测试集合训练集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25) ''' 实例化(数学解解法) ''' reg = LinearRegression() # 进行训练 reg.fit_normal(X_train, y_train) # LinearRegression() # 预测r方值 print('预测精确度', reg.score(X_test, y_test)) # 截距 print('数学解系数reg.coef_', reg.coef_) print('数学解截距reg.interception_', reg.interception_) print('数学解预测值', reg.predict(X_test)) print('y_test测试值', y_test) ''' 实例化(梯度下降解法) ''' lin_reg = LinearRegression() # 设置随机种子 np.random.seed(666) # 用一维数据方便可视化,但实际是对矩阵进行操作 x = np.random.random(size = 100) y = x * 3. + 4. + np.random.normal(size = 100) # 噪音:均值为0,方差为1 X = x.reshape(-1, 1) # 数组array中的方法,作用是将数据重新组织成100行1列的数据 # 梯度下降法进行训练 lin_reg.fit_gd(X, y) # LinearRegression() print('梯度下降系数reg.coef_', lin_reg.coef_) print('梯度下降截距reg.interception_', lin_reg.interception_)
运行结果
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。