目录
实验前准备
本实验是在Anaconda下的jupyter notebook上进行的,使用的代码语言为python。在开始实验前,我们首先需要导入所需要的库与包或者模块。本实验是一个逻辑回归实验,需要处理大量的实验数据,需要处理多维数组对象,以及需要画图进行可视化处理,还有一些数学公式的运用,所以我们需要导入的包为numpy、pandas、math以及matplotlib.pyplot。
代码实现为:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import math
import matplotlib as mpl
# ignore warnings
import warnings
warnings.filterwarnings('ignore')
逻辑回归——两个特征值
实验要求
实验数据预处理
我们首先使用pandas读取训练集的数据以及测试集的数据,然后检查数据中是否有数值为0等不合理的数据以及缺失的值,如果有则需要先将数据中不合理的值替换为缺失值,然后再将这些空值替换为相对应分类的平均值。而且由于标记值为字符串类型,所以我们需要将字符串类型的数据转换为0-1标记的整数类型。而在最后我们还需要将我们训练集的数据的散点图画出来,进行观察。而在随后的模型训练中,我们将偏置量b当成w0,所以我们还需要在训练集以及测试集的数据前引(插)入一列X0=1的特征值。
代码实现为:
#your code here------
# 通过pandas将csv文件转换为dataframe格式后进行操作
flower_train_frame=pd.read_csv('C:/Users/实验三(逻辑回归)/flower_train.csv')
flower_test_frame=pd.read_csv('C:/Users/实验三(逻辑回归)/flower_test.csv')
# 打印结果
print(flower_train_frame)
#可发现数据异常值中有空值(即NAN)
#判断每一列是否有空值并返回每列空值的数量
print(flower_train_frame.isnull().sum())
#由于异常数据中没有0,所以我们可直接考虑将空值替换为平均值。
# 先分离x1列和x2列
x1_column = flower_train_frame['x1']
x2_column = flower_train_frame['x2']
# 按类型type分类并计算每组的平均值
mean_x1_by_type = flower_train_frame.groupby('type')['x1'].transform('mean')
mean_x2_by_type = flower_train_frame.groupby('type')['x2'].transform('mean')
# 使用每个类型组的平均值来替换缺失值
flower_train_frame['x1'].fillna(mean_x1_by_type, inplace=True)
flower_train_frame['x2'].fillna(mean_x2_by_type, inplace=True)
# 打印结果
print(flower_train_frame)
# 由于type为字符串类型,所以我们需要将训练集以及测试集中的type转换为适合模型训练的整数类型
flower_train_frame['type'] = np.where(flower_train_frame['type'] == "Iris-setosa", 0, 1)
flower_test_frame['type']=np.where(flower_test_frame['type']=="Iris-setosa",0,1)
print(flower_train_frame)
print(flower_test_frame)
# 可视化散点图
# 可使用plt.scatter来绘制出测试集的每个样本点,并设置指定颜色来区分预测正确和错误的样本
# 在这里我们设置类型为0的设为蓝色,类型为1的设为红色
class_0=flower_train_frame[flower_train_frame['type']==0]
class_1=flower_train_frame[flower_train_frame['type']==1]
plt.scatter(class_0['x1'],class_1['x2'],c='blue')
plt.scatter(class_1['x1'],class_0['x2'],c='red')
plt.xlabel("X1")
plt.ylabel("X2")
plt.title("Logistic Regression")
plt.show()
#your code here------
# 首先通过pandas的Series函数创建一个全为1的ones,其中索引为相对应的训练集或者测试集的索引
ones_train=pd.Series(1, index=flower_train_frame.index)
ones_test=pd.Series(1,index=flower_test_frame.index)
# 在训练集flower_train_frame以及测试集flower_test_frame前面添加全为1的x0列
flower_train_frame.insert(0, 'x0', ones_train)
flower_test_frame.insert(0,'x0',ones_test)
# 打印添加x0特征后的frame
print(flower_train_frame)
print(flower_test_frame)
散点图如下所示:
实验结果分析:通过观察以上的散点图,我们可以发现在这两个特征下有明显的分类,所以理论上我们是可以运用逻辑回归进行分类的。
算法原理
逻辑回归代码实现
#your code here------
# 首先将训练数据以及测试数据转换为矩阵的形式
flower_train=np.array(flower_train_frame)
flower_test=np.array(flower_test_frame)
# 定义一个逻辑函数
def sigmoid(z):
return np.exp(z)/(1+np.exp(z))
# 定义一个梯度下降算法的函数
def Gradient_Descent_1(data):
# 首先获取数据的行与列
m,n=data.shape
# 将X与Y从数据中分离出来
X=data[:,:n-1]
Y=data[:,n-1]
# 初始化超参数
w=np.ones(n-1)
learning_rate=0.01
max_iterations=50000
threshold=1e-9
# 开始迭代
for iterations in range(max_iterations):
# 定义一个临时变量,作为计算dw的中间变量
temp=np.zeros(n-1)
# 开始迭代小批量的数据
for i in range(m):
x_i=X[i]
y_i=Y[i]
temp+=(x_i*(sigmoid(np.dot(w.T,x_i))-y_i))
# 更新w
dw=temp/m
w-=learning_rate*dw
# 判断参数更新的幅度是否小于某个阈值
if np.abs(dw).all()<threshold:
break
# 完成训练
return w
# 开始训练模型
w=Gradient_Descent_1(flower_train)
print(w)
实验测试结果可视化
#your code here------
# 开始计算损失函数loss的值
def Loss(data):
# 首先计算数据的行与列
m,n=data.shape
# 将X与Y从数据中分离出来
X=data[:,:n-1]
Y=data[:,n-1]
# 利用梯度下降函数得到w
w=Gradient_Descent_1(data)
# 定义一个临时变量,作为计算loss的中间值
h3=0
# 开始迭代所有数据
for i in range(m):
h1=-np.dot(w.T,X[i])
h2=Y[i]*(1/(1+np.exp(h1)))+(1-Y[i])*sigmoid(h1)
h3+=np.log(h2)
# 得到损失函数loss
loss=-h3/m
return loss
# 开始计算损失函数的值
loss=Loss(flower_train)
print("损失函数的值为:",loss)
#your code here------
# 开始计算损失函数的值
loss=Loss(flower_test)
print("损失函数的值为:",loss)
# 定义图像的边界
x1_max = np.max(flower_test[:, 1]) + 0.5
x1_min = np.min(flower_test[:, 1]) - 0.5
x2_max = np.max(flower_test[:, 2]) + 0.5
x2_min = np.min(flower_test[:, 2]) - 0.5
# 使用numpy中的meshgrid生成网格矩阵,方便进行之后的描点
boundary_x, boundary_y = np.meshgrid(np.arange(x1_min, x1_max, 0.01), np.arange(x2_min, x2_max, 0.01))
grid = np.c_[boundary_x.ravel(), boundary_y.ravel()]
# 加入偏置(或w_0)对应的特征为1的一列
grid = np.c_[np.ones((len(grid), 1)), grid]
# 计算出网格点中每个点对应的逻辑回归预测值
w=Gradient_Descent_1(flower_train)
z = sigmoid(grid.dot(w))
z[z < 0.5] = 0
z[z >= 0.5] = 1
# 转换shape以作出决策边界
z = z.reshape(boundary_x.shape)
plt.contourf(boundary_x, boundary_y, z, cmap=plt.cm.Spectral, zorder=1)
# 使用plt.scatter来绘制出测试集的每个样本点,并设置指定颜色来区分预测正确和错误的样本
# plt.scatter(x,y,c="color"),x、y为坐标值,c为指定颜色
class_1 = flower_test_frame[flower_test_frame['type'] == 1]
class_0 = flower_test_frame[flower_test_frame['type'] == 0]
plt.scatter(class_1['x1'], class_1['x2'], c='blue', zorder=1)
plt.scatter(class_0['x1'], class_0['x2'], c='green', zorder=1)
plt.xlabel("x1")
plt.ylabel("x2")
plt.title("Logistic Regression")
plt.show()
实验结果分析:如图所示,我们首先通过上面算法介绍的损失函数来计算训练集的损失函数的值,并且可以得知计算得到的训练集的损失函数的值为0.03990530073739535,可以得知,我们前面逻辑回归求得的参数w0、w1、w2是比较拟合的,逻辑回归方程也是比较符合预期的。随后我们将编写的算法与测试集进行检验,然后继续计算测试集的损失函数的值,计算得知测试集的损失函数的值为0.1250485831350379。然后我们需要在画出测试集的散点图的同时将决策边界描绘出来,具体的代码实现如上所示。所绘制出来的散点图以及决策图像如上图所示,我们观察发现两种类别的flower我们使用了不同颜色的点来标记出来了,同时决策边界也可以很好地将这两种类型的点区分开来,达到了实验的预期目标。
逻辑回归——多个特征值
实验要求
数据预处理
我们首先需要使用pandas将数据从文件中读入到dataframe中,然后使用numpy将其转换为矩阵的形式。由于本次实验的数据集中没有空缺值、不合理值以及标记的数据类型已经为0-1整数类型,所以不需要对数据进行前述的预处理。但是我们首先需要在这7个特征中选出4个以上的特征来进行训练,而在这里我选择的是Age,Sex,sibsp,Parch,Pclass,所以我们首先需要将这5列数据以及标记的数据从原矩阵中提取出来。同时,我们在训练模型是,还需要将偏置量b当作w0,所以我们在训练模型前需要在训练集以及测试集前插入一列x0=1的数据值。
代码实现为:
#your code here------
# 通过pandas将csv文件转换为dataframe格式后进行操作
train_titanic_frame=pd.read_csv('C:/Users/实验三(逻辑回归)/train_titanic.csv')
test_titanic_frame=pd.read_csv('C:/Users/实验三(逻辑回归)/test_titanic.csv')
# 打印结果
print(train_titanic_frame)
# 将以上数据转换为矩阵的形式
train_titanic=np.array(train_titanic_frame)
test_titanic=np.array(test_titanic_frame)
# 从七个特征值中选取五个特征值,分别为Age,Sex,sibsp,Parch,Pclass
# 将以上五个特征值所在的列提取出来
new_train_titanic=train_titanic[:,[1,3,4,5,6,7]]
new_test_titanic=test_titanic[:,[1,3,4,5,6,7]]
# 最后需要在训练集以及测试集矩阵的前面插入一列x0=1的数据(每运行一次都会加一列x0=1)
new_train_titanic=np.insert(new_train_titanic,0,1,axis=1)
new_test_titanic=np.insert(new_test_titanic,0,1,axis=1)
print(new_train_titanic)
print(new_test_titanic)
逻辑回归代码实现
# 定义一个梯度下降算法的函数,使用小批量梯度下降算法
def Gradient_Descent_2(data):
# 首先获取数据的行与列
m,n=data.shape
# 将X与Y从数据中分离出来
X=data[:,:n-1]
Y=data[:,n-1]
# 初始化超参数
w=np.ones(n-1)
learning_rate=0.01
max_iterations=100000
threshold=1e-9
batch_size=5
# 开始迭代
for iterations in range(max_iterations):
# 随机选取小批量数据的序号
batch_indices=np.random.choice(len(Y),size=batch_size,replace=False)
# 定义一个临时变量,作为计算dw的中间变量
temp=np.zeros(n-1)
# 开始迭代小批量的数据
for i in batch_indices:
x_i=X[i]
y_i=Y[i]
temp+=(x_i*(sigmoid(np.dot(w.T,x_i))-y_i))
# 更新w
dw=temp/batch_size
w-=learning_rate*dw
# 判断参数更新的幅度是否小于某个阈值
if np.abs(dw).all()<threshold:
break
# 完成训练
return w
# 开始训练模型
w=Gradient_Descent_2(new_train_titanic)
print(w)
实验结果分析:由于本次实验的数据集样本数量比较大,所以我们应该使用的是小批量或者随机梯度下降算法来实现这一个逻辑回归算法;而在前面的实验中,由于数据集样本数量比较少,所以我们可以直接使用批量梯度下降法来实现逻辑回归的算法。所以我们需要使用小批量梯度下降法来另外编写这一个算法实现。具体的代码实现如上所示。
实验测试结果可视化
#your code here------
# 调用前面的梯度下降函数对测试集进行训练
w=Gradient_Descent_2(new_test_titanic)
# 调用前面的loss函数
loss=Loss(new_test_titanic)
print("损失函数的值为:",loss)
实验结果分析:如图所示,我们直接调用前面所写好的梯度下降函数Gradient_Descent_2对测试集进行逻辑回归训练即可,最后我们还需要调用Loss函数来计算损失函数的值。如图所示,我们计算得到的损失函数的值为0.42891373089277957。
实验总结
逻辑回归是一种广义线性模型,将线性回归模型的输出通过一个非线性函数(通常是sigmoid函数)进行映射,得到一个概率值。
逻辑回归适用于二分类问题,可以估计输入变量与输出变量之间的概率关系,而不仅仅是简单的分类结果。
逻辑回归模型的参数通常是通过最大似然估计等方法进行训练得到的。
在本次实验中,在使用梯度下降法实现逻辑回归代码时,我觉得需要注意的事项主要有:
1. 特征缩放:在使用梯度下降法之前,通常需要对特征进行缩放,以确保不同特征的取值范围差异不会对梯度下降算法产生不良影响。
2. 学习率选择:梯度下降法中的学习率是一个重要的超参数,需要选择一个合适的学习率,以保证算法能够快速收敛到最优解。学习率过大可能导致算法发散,学习率过小可能导致收敛速度过慢。
3. 收敛判断:在实现梯度下降法时,需要设定一个收敛判断条件,例如设置一个阈值,当损失函数的变化小于阈值时,认为算法已经收敛,可以停止迭代。
4. 正则化:为了防止过拟合,逻辑回归模型通常会使用正则化项(如L1正则化或L2正则化)来控制模型复杂度。在实现时,需要注意将正则化项加入梯度下降算法中。
在本次逻辑回归的实验中,我深入学习到了逻辑回归以及线性回归的结合算法以及具体的代码实现,对逻辑回归有了进一步的了解。同时学习到了:①特征工程:实验中可能需要进行特征选择、特征缩放、特征组合等操作,以提高模型的性能。②模型评估:实验中需要使用适当的评估指标来评估模型的性能,如准确率、精确率、召回率等。③超参数调优:实验中可能需要调优模型的超参数,如学习率、正则化参数等,以获得更好的模型性能。④解释模型结果:逻辑回归模型的参数可以帮助解释输入变量对输出变量的影响程度,实验中可以通过分析参数值来解释模型的结果。