问题描述:
您将使用一个变量实现线性回归,以预测食品卡车的利润。假设你是一家连锁餐厅的首席执行官,正在考虑在不同的城市开一家新餐厅。该连锁店已经在多个城市拥有卡车,你可以从这些城市获得利润和人口数据。你想使用这些数据来帮助你选择下一个城市。文件ex1data1.txt包含我们的线性回归问题的数据集。第一列是一个城市的人口第二列是该城市一辆快餐车的利润。利润为负数表示亏损。
问题分析:
上述目的就是根据人口来预测利润,对于ex1data1.txt中的数据集,第一列的数据是人口,第二列的数据是利润,我们要做的就是找到一个回归方程来拟合这些数据,对于每一个给定的人口数据,能够较好的预测出与之对应的利润,本问题中采用两个参数 和 ,一次方程的方法进行拟合
分析过程:
1、假设函数:
在本问题中,我们的最终目的就是找到一个假设函数 ,使之能够较好的满足给定的数据集
2、代价函数:
对于假设函数,一个重要的要求就是尽可能地使得他的代价值最小,代价函数为
而我们要求地是这个方程地最小值,直接求计算量太大而且复杂,所以使用梯度下降算法,就是从一个点出发,计算该点地梯度(梯度地定义为该点下降最快地方向,就是朝着不同维度正方向地偏导,这个问题就是分别对 求偏导,然后减去学习率乘以这两个偏导值,不断更新重复这个过程,直到偏导值为分别为0,方程如下
函数J我们已知,为了编程方便,引入变量,使其一直为1
对 求导得
对 求导得
综合来看,可以看成,对 (j=0,j=1)求导得
所以迭代方程如下
代码实现:
import pandas as pd
import numpy as np
from part_of_work.property import properties
# 读取数据,且索引值为population和profit
p1 = properties()
ex1data1 = p1.ex1data1
df1 = pd.read_csv(ex1data1, names=['population', 'profit'])
# 读取特征,自变量叫做特征,也就是x
def get_x(df):
ones = pd.DataFrame({'ones': np.ones(len(df))})
data = pd.concat([ones, df], axis=1) # 按照列合并数据
return np.matrix(data.iloc[:, :-1]) # 以数组的形式返回除了最后一行的数据,也就是第一列全1和第二列population的数据
# 读取标签,因变量叫做标签,也就是y
def get_y(df):
return np.matrix(df.iloc[:, df.shape[1] - 1:]) # 返回最后一列的数据,也就是y的数据,profit的数据
# 特征缩放,由于特征在范围较大的尺度的时候,梯度下降的速度会很慢,所以将所有数据转化到-1,1之间
def normalize_feature(df):
return df.apply(lambda column: (column - column.mean()) / column.std()) # 值减去均值除以标准差
# 计算代价函数
"""
本来的方程是
h = th_0 + th_1 * x1
但是为了描述方便,将方程变为 h = th_0 * x0 + th_1 * x1
其中,x0 永远为1
"""
def compute_cost(x1, y1, theta1):
inner = np.power((x1 * theta1.T - y1), 2) # 求代价的平方和函数,每一个对应得到的值与实际的相减
return np.sum(inner) / (2 * x1.shape[0]) # 为了方便求导,除以2乘以数据的长度
"""
DataFrame函数
shape[0]中存储的是这个数据的高度
shape[1]中存储的是这个数据的宽度,不包含索引,单纯的数据
shape是查看尺寸
.T表示对一个矩阵进行转秩
"""
x1 = get_x(df1)
y1 = get_y(df1)
# 生成theta,theta是一行二列 1*2的矩阵
theta1 = np.matrix(np.array([0, 0]))
def gradient_decent(x, y, theta, alpha, iters):
temp = np.matrix(np.zeros(theta1.shape)) # 建立1*2的,全为0的矩阵
parameters = int(theta1.ravel().shape[1]) # 赋值为参数的个数多少,本问题中为2
cost = np.zeros(iters) # 建立一个迭代次数大小的全0矩阵
for i in range(iters): # 进行迭代,理论上只要迭代次数足够多一定会有结果
error = (x * theta.T) - y # 计算当前取值的theta对应的y和实际的差距,得到的是一个样本容量大小的一维矩阵
for j in range(parameters): # 分别对两个参数theta_0和theta_1进行处理
term = np.multiply(error, x[:, j]) # x_0 * (x_0 * theta_0 + x_1 * theta_1 - y),就是求导公式中要求和的那一串
temp[0, j] = temp[0, j] - (alpha/len(x)) * np.sum(term) # 这个就是函数的更新过程theta值的更新
pass
theta = temp # 赋值给theta
cost[i] = compute_cost(x, y, theta)
pass
return theta, cost
theta2, cost2 = gradient_decent(x1, y1, theta1, alpha=0.01, iters=1000)
print(theta2)
print(np.min(cost2))