- Published on
机器学习 | 逻辑回归简单操作
- Authors

- Name
- Jiaqi Wang(Ramis)
[ 最后更新 于 2024-06-26 ] 祝贺失踪鹅仔回归!(??)
逻辑回归 简单代码实现
这篇练习记录一下logistic regression的代码实操以及简单的数据分析的一般流程
使用的是Kaggle上的数据:https://www.kaggle.com/datasets/fsadjadpour/binary/data
Graduate Admission 研究生录取预测
1. Data Import and Exploration
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
通过pandas读取csv文件, 这里也可以使用csv的url的链接
raw_df=pd.read_csv("Graduate Admission.csv")
首先熟悉数据,用head查看前十条数据 用.tail(n)可以查看最后n条,用describe()返回数据基本的统计特征值概览
raw_df.head(10)
raw_df.describe()
尝试用seaborn的boxplot画了一个箱型图来观察一下录取结果和GPA,GRE以及排名之间的联系。可以看出GRE和GPA都和录取结果呈一定的正相关,而虽然不知道具体rank所指是什么为什么只有1到3,但是排名在1到2之间录取结果是几乎是板上钉钉。因此这三个量都应当可以作为逻辑回归的参数。
fig, axes = plt.subplots(1, 3, figsize=(12, 6))
colors = ["#ed45b2", "#87e8c1"]
for i in range(3):
sns.boxplot(x='admit', y=raw_df.columns[i+1], data=raw_df, ax=axes[i],palette=colors)
f_heading=raw_df.columns[i+1].upper()
axes[i].set_title('Admission Status by '+f_heading)
axes[i].set_xlabel('Admission Status (0: Not Admitted, 1: Admitted)')
axes[i].set_ylabel(raw_df.columns[i+1])
# Adjust layout
plt.tight_layout()
plt.show()

2. 数据预处理 Data Pre-Processing
此时我们将pandas中的数据转入numpy:
X_input = raw_df[['gre', 'gpa', 'rank']].to_numpy()
Y_input = raw_df['admit'].to_numpy()
进行标准化:
def z_score(data):
mean=np.mean(data,axis=0) #注意此处 axis=0 表示按照列汇总——>也就是说 mean的结果与行格式相同
std=np.std(data,axis=0)
return (data-mean)/std
X_train=z_score(X_input)
Y_train=Y_input
可以看到此时的训练集X数字都按比例放缩了,这便于接下来进行梯度递降。
X_train
array([[-1.80026271, 0.57907221, 0.54596793],
[ 0.62666824, 0.73692924, 0.54596793],
[ 1.84013372, 1.60514292, -1.57429586],
...,
[-1.10685387, -1.99925931, -0.51416397],
[ 0.97337266, 0.68431023, -0.51416397],
[ 0.10661161, 1.31573836, 0.54596793]])
3. 梯度递降法实现逻辑回归
要开始梯度递降首先要准备三个函数:
其一是此前介绍过的sigmoid函数,这是逻辑回归的核心函数,用来估算概率。
def sigmoid(z):
return(1/(1+np.exp(-z)))
其二,定义一个compute_cost函数来求cost function的值。因为每次梯度下降更新w和b之后都要重新计算一次cost以便比较。
def compute_cost(X,y,w,b,*argv):
m,n=X.shape
cost=0
for i in range(0,m):
z=np.dot(w,X[i])+b
f=sigmoid(z)
loss=-y[i]*np.log(f)-(1-y[i])*np.log(1-f)
cost+=loss
cost=cost/m
return cost
最后还要定义compute_graduate函数来求每次下降时要更新的梯度。 需要注意,此处w是向量,更新的过程中对向量的每一维都需要分别求值。
def compute_gradient(X,y,w,b,*argv):
m,n=X.shape
dj_dw=np.zeros(w.shape)
dj_db=0
for i in range(0,m):
z_wb=np.dot(w,X[i])+b
f_wb=sigmoid(z_wb)
dj_db_i=f_wb-y[i]
dj_db+=dj_db_i
for j in range(n):
dj_dw[j]+=dj_db_i*X[i][j]
dj_dw=dj_dw/m
dj_db=dj_db/m
return dj_dw,dj_db
有了这三个函数,可以开始进行梯度递降的常规操作,这个过程和之前线性函数中的没什么不同。
def gradient_descent(X,y,w_in,b_in,cost_function,gradient_function,alpha,itrs,lambda_):
m=len(X)
J_history=[]
w_history=[]
for i in range(itrs):
if i<100000:
cost=compute_cost(X,y,w_in,b_in,lambda_)
J_history.append(cost)
if (i%math.ceil(itrs/10)==0) or (i==itrs-1):
w_history.append(w_in)
print(f"Iteration {i:4}: Cost {float(J_history[-1]):8.2f} w:{w_in}")
dj_dw,dj_db=gradient_function(X,y,w_in,b_in,lambda_)
w_in=w_in-alpha*dj_dw
b_in=b_in-alpha*dj_db
return w_in,b_in,J_history,w_history
初始化后开始运算。
np.random.seed(1)
initial_w = 0.01 * (np.random.rand(3) - 0.5)
initial_b = 0
# Some gradient descent settings
iterations = 10000
alpha = 0.01
w,b, J_history,w_history = gradient_descent(X_train,Y_train, initial_w, initial_b, compute_cost, compute_gradient, alpha, iterations, 0)
#返回结果:
Iteration 0: Cost 0.69 w:[-0.00082978 0.00220324 -0.00499886]
Iteration 1000: Cost 0.58 w:[ 0.24319593 0.25340642 -0.43003393]
Iteration 2000: Cost 0.57 w:[ 0.26287299 0.28477028 -0.50475713]
Iteration 3000: Cost 0.57 w:[ 0.26502824 0.29237737 -0.52258257]
Iteration 4000: Cost 0.57 w:[ 0.26496915 0.2944698 -0.52689643]
Iteration 5000: Cost 0.57 w:[ 0.2647973 0.29507623 -0.5279349 ]
Iteration 6000: Cost 0.57 w:[ 0.26471095 0.29525696 -0.52818506]
Iteration 7000: Cost 0.57 w:[ 0.26467699 0.29531164 -0.52824578]
Iteration 8000: Cost 0.57 w:[ 0.26466489 0.29532831 -0.52826072]
Iteration 9000: Cost 0.57 w:[ 0.26466079 0.29533342 -0.52826447]
Iteration 9999: Cost 0.57 w:[ 0.26465945 0.29533499 -0.52826542]
可以看到结果停留在了cost大约在0.57的位置。
def predict(X, w, b):
m, n = X.shape
p = np.zeros(m)
for i in range(m):
z_wb = 0
for j in range(n):
z_wb += X[i][j]*w[j]
z_wb += b
f_wb = sigmoid(z_wb)
if f_wb>=0.5: p[i]=1
else: p[i]=0
return p
我们可以计算y hat的值来看看这个模型的正确率是多少:
p = predict(X_train, w,b)
print('Train Accuracy: %f'%(np.mean(p == Y_train) * 100))
Train Accuracy: 70.500000
小结:
在重度拖延中提交这一篇。本来后面还想写一写正则化和模型选用的问题,但是感到还有很多要看的东西,所以干脆推到下一篇吧~(?)
这次的代码是按照课程笔记以及理解一点点纯手敲再对照讲义debug的,感觉很简单实际写下来还是需要很多细心呀。再次感叹还是做调包侠舒服…可惜这段时间还要先把numpy和pandas用熟了。
最近也已经把神经网络最简单的原理篇看完了,感觉这个东西实在太暴力啦。真就是到了算力到了就是可以为所欲为的世界呢…或许这周末也可以开始动笔写原理篇了。
再有就是想整理一个pandas实用方法合集;) 最近的to do也太多啦…跑走

此处 碎碎念
-鹅仔 2024/06/26 21:08