1、看了吴恩达机器学习课程关于线性回归的讲述,这个文章是对应该课程的线性回归练习
2、代码是看了网上有人分享的线性回归的python版本实现,这篇文章是结合代码做了讲解
3、相关附件已上传到个人下载部分。
# 建立虚拟环境
conda create -n exec_py36 pip python=3.6
# 1、打开Anaconda prompt,并激活虚拟环境
conda activate exec_py36
# 2、安装ipykernel,用于操控jupyter内核
pip install ipykernel -i https://pypi.douban.com/simple #使用了豆瓣源
# 3、将虚拟环境引入jupyter notebook
python -m ipykernel install --user --name exec_py36 --display-name "Python [conda env:exec_py36]"
import os
print(os.path.abspath('.'))
# 输出如下:
# C:\Users\yan
# 进入anaconda prompt,并输入下面的命令
jupyter notebook --generate-config
# 得到如下输出:
# Writing default config to: C:\Users\yan\.jupyter\jupyter_notebook_config.py
# c.NotebookApp.notebook_dir = ''
,将这一行的注释删掉,然后在单引号中填写自己新建的文件夹的路径,然后保存。"%USERPROFILE%/"
,然后点击“应用”,“确定”,最后重新启动Jupyte Notebook即可。数据集介绍:第一列是每个城市的人口,第二列是每个城市一卡车食物的利润
# 我是在anaconda prompt里面安装的
conda install pandas
conda install matplotlib
conda install seaborn
在安装pandas的时候会顺带安装numpy,所以就没有再次安装numpy了(其实安装matplotlib时也是会自动安装pandas和numpy的):
import pandas as pd
df = pd.read_csv('ex1data1.txt', names=['population', 'profit']) # 读取数据并赋予列名
df.head() # 看前五行
df.info() # 查看数据信息
# 结果如下图所示
import seaborn as sns
sns.set(context="notebook", style="whitegrid", palette="dark") # 设置画图的一些基本配置
import matplotlib.pyplot as plt
# 由于数据只有两列,因此可以使用散点图可视化一下数据,看看是什么样子
sns.lmplot('population', 'profit', df, height=6, fit_reg=False)
plt.show()
# 结果如下图所示,由图可知,数据的分布大致呈现一条直线,所以接下来会采用线性回归进行拟合
h θ ( x ) = θ 0 + θ 1 x 1 + θ 2 x 2 + . . . + θ n x n (1) h_\theta(x)=\theta_0+\theta_1x_1+\theta_2x_2+...+\theta_nx_n \tag{1} hθ(x)=θ0+θ1x1+θ2x2+...+θnxn(1)
h θ ( x ) = θ 0 x 0 + θ 1 x 1 + θ 2 x 2 + . . . + θ n x n (2) h_\theta(x)=\theta_0x_0+\theta_1x_1+\theta_2x_2+...+\theta_nx_n \tag{2} hθ(x)=θ0x0+θ1x1+θ2x2+...+θnxn(2)
h θ ( x ) = θ T X (3) h_\theta(x)=\theta^TX \tag{3} hθ(x)=θTX(3)
# 读取特征
def get_X(df):
# """
# use concat to add intercept term to avoid side effect
# not efficient for big dataset though
# """
ones = pd.DataFrame({'ones': np.ones(len(df))}) # ones是m行1列的dataframe
data = pd.concat([ones, df], axis=1) # 合并数据,根据列合并
return data.iloc[:, :-1].as_matrix() # 这个操作获取所有的特征列,返回 ndarray,不是矩阵
# 读取标签
def get_y(df):
# """
# assume the last column is the target
#
# """
return np.array(df.iloc[:, -1]) # df.iloc[:, -1]是指df的最后一列
X = get_X(df)
print(X.shape, type(X)) # 看下数据维度
y = get_y(df)
print(y.shape, type(y))
# 结果如下
# (97, 2)
# (97,)
# 由线性回归假设函数可知,参数向量的维数是原始数据集的特征数+截距项的特征数
# 在本示例单变量线性回归中,参数向量维数就是1+1=2
theta = np.zeros(X.shape[1]) # X.shape[1]=2,代表特征数n
print(theta)
# 结果如下
# [ 0. 0.]
J ( θ ) = 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 (4) J(\theta)=\frac{1}{2m}\sum\limits_{i=1}^m{({h_\theta(x^{(i)})-y^{(i)}})^2} \tag{4} J(θ)=2m1i=1∑m(hθ(x(i))−y(i))2(4)
其中: h θ ( x ) = θ T X = θ 0 x 0 + θ 1 x 1 h_\theta(x)=\theta^TX=\theta_0x_0+\theta_1x_1 hθ(x)=θTX=θ0x0+θ1x1。
# 定义代价函数
def lr_cost(theta, X: numpy.ndarray, y: numpy.ndarray):
'''
:param theta: 维度是R(n),是线性回归的参数
:param X: 维度是R(m*n),m为样本数,n为特征数
:param y:维度是R(m)
:return:
'''
m = X.shape[0] # m为样本数
# 计算每个样本的每个特征与对应参数的乘积
inner = X.dot(theta) - y # X.dot(theta)等价于np.dot(X,theta),inner的维度是R(m*1)
# 计算代价函数里的平方,然后求和,需要注意:
# 1*m @ m*1 = 1*1 in matrix multiplication
# but you know numpy didn't do transpose in 1d array, so here is just a
# vector inner product to itselves
square_sum = np.dot(inner.T, inner) # square_sum维度是R(1*1)
cost = square_sum / (2 * m)
return cost
lr_cost(theta, X, y) # 试一试初始的参数对应的代价是多少
# 结果如下
# 32.072733877455669
i n n e r ( m , 1 ) = X ( m , n + 1 ) . d o t ( θ ( n + 1 , 1 ) ) − y ( m , 1 ) (5) inner_{(m,1)}=X_{(m,n+1)}.dot(\theta_{(n+1,1)})-y_{(m,1)} \tag{5} inner(m,1)=X(m,n+1).dot(θ(n+1,1))−y(m,1)(5)
s q u a r e _ n u m ( 1 , 1 ) = ( i n n e r . T ) ( 1 , m ) . d o t ( i n n e r ( m , 1 ) ) (6) square\_num_{(1,1)}=(inner.T)_{(1,m)}.dot(inner_{(m,1)}) \tag{6} square_num(1,1)=(inner.T)(1,m).dot(inner(m,1))(6)
θ j = θ j − α ∂ ∂ θ j J ( θ ) (7) \theta_j=\theta_j-\alpha\frac{\partial}{\partial\theta_j}J(\theta) \tag{7} θj=θj−α∂θj∂J(θ)(7)
θ j = θ j − α 1 m ∑ i = 1 m ( ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) ) (8) \theta_j=\theta_j-\alpha\frac{1}{m}\sum\limits_{i=1}^m((h_\theta(x^{(i)})-y^{(i)})x^{(i)}_j) \tag{8} θj=θj−αm1i=1∑m((hθ(x(i))−y(i))xj(i))(8)
# 先定义函数来计算梯度下降更新公式中的求和部分
def gradient(theta, X, y):
'''
:param theta: 维度是R(n),是线性回归的参数
:param X: 维度是R(m*n),m为样本数,n为特征数
:param y: 维度是R(m)
:return:维度是R(n+1,1),即与参数向量theta同维度
'''
m = X.shape[0]
inner = np.dot(X.T, (np.dot(X, theta) - y))
return inner / m
i n n e r ( n + 1 , 1 ) = ( X ( m , n + 1 ) ) T . d o t ( ( X ( m , n + 1 ) . d o t ( θ ( n + 1 , 1 ) ) − y ( m , 1 ) ) ) (9) inner_{(n+1,1)}=(X_{(m,n+1)})^T.dot((X_{(m,n+1)}.dot(\theta_{(n+1,1)})-y_{(m,1)})) \tag{9} inner(n+1,1)=(X(m,n+1))T.dot((X(m,n+1).dot(θ(n+1,1))−y(m,1)))(9)
# 批量梯度下降函数
def batch_gradient_decent(theta, X, y, epoch, alpha=0.01):
'''
:param theta: 维度是R(n),是线性回归的参数
:param X: 维度是R(m*n),m为样本数,n为特征数
:param y: 维度是R(m)
:param epoch: 批处理的轮数
:param alpha: 学习率,即梯度下降更新公式里的alpha
:return: 拟合线性回归,返回参数和代价
'''
cost_data = [lr_cost(theta, X, y)]
_theta = theta.copy() # 拷贝一份,不和原来的theta混淆
for _ in range(epoch):
_theta = _theta - alpha * gradient(_theta, X, y)
cost_data.append(lr_cost(_theta, X, y))
return _theta, cost_data
epoch = 500
final_theta, cost_data = batch_gradient_decent(theta, X, y, epoch)
print(final_theta)
# 结果如下
# [-2.28286727 1.03099898]
ax = sns.lineplot(cost_data, y=np.arange(epoch+1))
ax.set_xlabel('epoch')
ax.set_ylabel('cost')
plt.show()
# 观察最终的拟合曲线
b = final_theta[0] # intercept,Y轴上的截距
m = final_theta[1] # slope,斜率
plt.scatter(df.population, df.profit, label="Training data")
plt.plot(df.population, df.population*m + b, label="Prediction")
plt.legend(loc=2)
plt.show()
图解Windows10下如何更换Jupyter Notebook 内核Python版本(切换原始的python环境) - Python研究者 - 博客园 (cnblogs.com)
Jupyter notebook文件默认存储路径以及更改方法 (360doc.com)
Python的NumPy库中dot()函数详解_阿凌sara的博客-CSDN博客_python中dot函数