逻辑回归(Logistic regression)
三种梯度下降策略:
- 批量梯度下降:容易得到最优解,但是由于每次考虑所有样本,速度很慢
- 随机梯度下降:每次找一个样本,迭代速度快,但不一定每次都朝着收敛的方向
- 小批量梯度下降:每次更新选择一小部分数据来算,实用
对于 逻辑回归 不了解的,可以看看我写的这篇文章,或许可以帮助到你。URL: 逻辑回归算法-推导学习详细
案例:
- 根据学生的两门课成绩,决定学生是否被录取
- 部分测试数据
Exam 1 | Exam 2 | Admitted |
34.62 | 78.24 | 0 |
30.28 | 43.89 | 0 |
35.84 | 72.90 | 0 |
60.18 | 86.30 | 1 |
79.03 | 75.34 | 1 |
代码:
import os
import time
import numpy as np
import pandas as pd
STOP_ITER = 0
STOP_COST = 1
STOP_GRAD = 2
def stopCriterion(type, value, threshold): # 迭代次数 thresh=5000
"""
设定三种不同的停止策略
:param type: 停止策略类型
:param value:
:param threshold: 阈值
:return:bool值
"""
if type == STOP_ITER:
return value > threshold
elif type == STOP_COST:
return abs(value[-1] - value[-2]) < threshold
elif type == STOP_GRAD:
return np.linalg.norm(value) < threshold
def get_data(Input_path):
"""
读取数据
:param Input_path: 数据所在的目录
:return: pd_data 把数据转成pandas返回,列名 ['Exam 1', 'Exam 2', 'Admitted']
"""
Input_path += os.sep # os.sep 打印'\\' os.sep根据你所处的平台,自动采用相应的分隔符号
file_name_list = os.listdir(Input_path) # 读取目录下的所有文件
print(file_name_list)
for file_name in file_name_list:
path = Input_path + file_name
break
print(path)
pd_data = pd.read_csv(open(path), header=None, names=['Exam 1', 'Exam 2', 'Admitted']) # 由于数据是没有列名的,所以自己指定
# positive = pd_data[pd_data['Admitted'] == 1]
# negative = pd_data[pd_data['Admitted'] == 0]
pd_data.insert(loc=0, column='Ones', value=1) # 新插入一列配合 θ0
orig_data = pd_data.as_matrix()
# orig_data = pd_data.values()
# print("orig_data.shape: {}".format(orig_data.shape)) # (5, 4)
# print("orig_data.shape[1]: {}".format(orig_data.shape[1])) # 4
return orig_data
def shuffleData(data):
"""
洗牌
:param data:原始训练数据
:return: X, y 原始训练数据(train set x)
"""
np.random.shuffle(data)
cols = data.shape[1]
X = data[:, 0:cols - 1]
y = data[:, cols - 1:]
return X, y
def sigmoid(z):
"""
sigmoid 函数
:param z: 输入值
:return: 1 / (1 + np.exp(-z)) 计算后的结果 原始训练数据(train set y)
"""
sigmoid_reslut = 1 / (1 + np.exp(-z))
print("***************************** sigmoid(z) **********************************")
# print("1 / (1 + np.exp(-z))计算结果为:{}".format(sigmoid_reslut))
return sigmoid_reslut
def model(X, theta):
"""
h(θ) = θT * x 的计算模块
:param X: 原始训练数据(train set x)
:param theta: θ值
:return: sigmoid(np.dot(X, theta.T)) 即 h(θ) 的计算结果,把计算结果映射到sigmoid函数里
"""
print("***************************** model(X, theta) h(θ) **********************************")
# print("X 为 :{}".format(X))
# print("np.dot(X, theta.T)计算结果为:{}".format(np.dot(X, theta.T)))
model_reslut = sigmoid(np.dot(X, theta.T))
return model_reslut
def cost(X, y, theta):
"""
损失函数
D(hθ(x),y)=−ylog(hθ(x))−(1−y)log(1−hθ(x))
J(θ)=1/n(D(∑hθ(xi),yi)) i=1,2,3....n
:param X: 原始训练数据(train set x)
:param y: 原始训练数据(train set y)
:param theta: θ值
:return: J(θ)
"""
left = np.multiply(-y, np.log(model(X, theta)))
right = np.multiply(1 - y, np.log(1 - model(X, theta)))
return np.sum(left - right) / (len(X))
def gradient(X, y, theta, i):
"""
计算梯度
对J(θ)求偏导
:param X: 原始训练数据(train set x)
:param y: 原始训练数据(train set y)
:param theta: θ值
:return: grad 梯度值
"""
grad = np.zeros(theta.shape)
print("第{}次初始grad:{}".format(i, grad))
error = (model(X, theta) - y).ravel()
for j in range(len(theta.ravel())): # for each parmeter theta.ravel() --》array([0., 0., 0.])
term = np.multiply(error, X[:, j]) # 因为原始数据我们加了一列1,而且是三个theta , 所以 X[:, j] 是取 X 的三列不同的数据
grad[0, j] = np.sum(term) / len(X)
print("第{}次更新grad:{}".format(i, grad))
return grad
def descent(data, theta, batchSize, stopType, thresh, alpha):
"""
梯度下降求解
:param data: 原始训练数据
:param theta: θ值 array([[0., 0., 0.]])
:param batchSize: batch数量个数据 batch = n = 100
:param stopType: 停止策略类型 STOP_ITER = 0 STOP_COST = 1 STOP_GRAD = 2
:param thresh: 迭代次数 5000
:param alpha: 学习率 0.000001
:return: theta, i-1, costs, grad, time.time() - init_time
"""
init_time = time.time() # 返回当前时间的时间戳
i = 0 # 迭代次数
k = 0 #
X, y = shuffleData(data) # 对数据进行 “洗牌”
# grad = np.zeros(theta.shape) # 计算的梯度
costs = [cost(X, y, theta)] # 损失值 costs:[0.6931471805599453]
print("初始计算时的 costs :{}".format(costs))
print("初始计算时的 theta :{}".format(theta))
while True:
print("***************************************************************".format(i))
print("************************第 {} 次循环计算************************".format(i))
grad = gradient(X[k:k + batchSize], y[k:k + batchSize], theta, i)
k += batchSize # 取batch数量个数据
if k >= n:
k = 0
X, y = shuffleData(data) # 数据重新“洗牌”
theta = theta - alpha * grad # θ参数更新
costs.append(cost(X, y, theta)) # 计算新的损失
print("第 {} 次计算的 costs :{}".format(i, cost(X, y, theta)))
print("第 {} 次计算的 theta :{}".format(i, theta))
i += 1
if stopType == STOP_ITER: # STOP_ITER = 0
value = i
elif stopType == STOP_COST: # STOP_COST = 1
value = costs
elif stopType == STOP_GRAD: # STOP_GRAD = 2
value = grad
# stopCriterion() 返回的是bool值,满足相应的条件就循环停止计算
if stopCriterion(stopType, value, thresh): # 迭代次数 thresh=5000
break
return theta, i - 1, costs, grad, time.time() - init_time
def run(data, theta, batchSize, stopType, thresh, alpha):
"""
控制程序
:param data: 原始训练数据
:param theta: θ值 array([[0., 0., 0.]])
:param batchSize: batch数量个数据 batch = n = 100
:param stopType: 停止策略类型
:param thresh: 迭代次数 thresh=5000
:param alpha: 学习率
:return: 学习好的θ值 array([[0., 0., 0.]])
"""
theta, iter, costs, grad, dur = descent(data, theta, batchSize, stopType, thresh, alpha)
# theta : θ值 array([[0., 0., 0.]])
# iter : 迭代次数
# costs : 每一次的损失值 是一个列表 可视化用
# grad :梯度值 也就是对 J(θ) 求偏导的值
# dur : 消耗的时间
return theta
if __name__ == '__main__':
Input_path = r"D:\Py_Machine_Learn\data\py_charm"
orig_data = get_data(Input_path)
n = 100 # batch数量个数据
theta = np.zeros([1, 3])
# 设定迭代次数停止策略
run(orig_data, theta, n, STOP_ITER, thresh=5, alpha=0.000001) # 基于所有的样本 这里thresh表示迭代次数
# 根据损失值停止
run(orig_data, theta, n, STOP_COST, thresh=0.000001, alpha=0.001) # 这里thresh表示损失值
# 根据梯度变化停止
run(orig_data, theta, n, STOP_GRAD, thresh=0.05, alpha=0.001) # 这里thresh表示梯度阈值