Logistic回归可以被看成是一种概率估计。我们在每个特征上乘一个回归系数,然后所有值相加,总和带入sigmoid函数,得到一个0~1之间的数值,大于0.5的被分到1类,小于0.5的被分到0类别中。
一、理论基础
- 用Logistic回归进行分类的
主要思想
是:根据现有数据对分类边界线建立回归公式,以此进行分类。“回归”源于最佳拟合,表示要找到的最佳拟合参数。 -
优点
:计算代价不高,易于理解和实现 -
缺点
:易欠拟合,分类精度可能不高 -
适用数据类型
:数值型和标称型 sigmoid
函数
- 梯度算子总是指向函数值变化最快的方向
- 梯度上升算法伪代码:
二、代码实现
2.1 加载数据
def load_dataset():
"""
读取拟合数据集和标签
:return: 数据集,标签
"""
data_list = []
label_list = []
with open('./testSet.txt', 'r') as f:
for line in f.readlines():
line_data = line.strip().split() # 去掉左右空格, 并按空格切分
data_list.append([1.0, float(line_data[0]), float(line_data[1])])
label_list.append(int(line_data[2]))
return data_list, label_list
2.2 梯度上升算法
def sigmoid(data):
"""
分类函数
:param data: 输入数据
:return: 分类概率
"""
return 1.0 / (1 + np.exp(-data))
def grad_asent(data_list, label_list):
"""
用梯度上升获取最佳拟合系数
:param data_list: 数据列表
:param label_list: 标签列表
:return: 最佳拟合系数
"""
data_mat = np.mat(data_list)
label_mat = np.mat(label_list).transpose()
r,c = np.shape(data_mat)
alpha = 0.001
max_cycle = 500
weights = np.ones((c, 1))
for i in range(max_cycle):
h = sigmoid(data_mat * weights)
loss = label_mat - h
weights += alpha * data_mat.transpose() * loss
return weights
2.3 随机梯度上升算法
算法的伪代码是:
def stoc_grad_asent(data_list, label_list, num_iter=150):
"""
随机梯度上升获取最佳拟合参数
:param data_list: 数据矩阵
:param label_list: 标签向量
:return: 最佳拟合参数
"""
data_mat = np.array(data_list)
r, c = np.shape(data_mat)
weights = np.ones(c)
for j in range(num_iter):
data_index = list(range(r))
for i in range(r):
alpha = 4 / (1.0+j+i)+0.01
rand_index = int(random.uniform(0, len(data_index)))
h = sigmoid(np.sum(data_mat[rand_index] * weights))
error = label_list[rand_index] - h
weights += alpha * error * data_mat[rand_index]
del (data_index[rand_index])
return weights
以上步长alpha每次迭代都会调整,这样会防止数据波动。
2.4 绘制最佳拟合曲线
def plot_best_fit(weights):
"""
画最佳拟合曲线
:param weights: 最优参数
:return:
"""
data_list, label_list = load_dataset()
data_array = np.array(data_list)
n = np.shape(data_list)[0]
x1 = []
y1 = []
x2 = []
y2 = []
for i in range(n):
if label_list[i] == 1:
x1.append(data_array[i, 1])
y1.append(data_array[i, 2])
else:
x2.append(data_array[i, 1])
y2.append(data_array[i, 2])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(x1, y1, s=30, c='red', marker='s')
ax.scatter(x2, y2, s=30, c='green')
x = np.arange(-3.0, 3.0, 0.1)
y = (-weights[0] - weights[1]*x)/ weights[2]
ax.plot(x, y)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
2.5 实际应用:预测病马死亡率
def colic_test():
"""`
在训练集训练初最佳参数
在测试集上获得错误率
:return: 测试集上的错误率
"""
train_set = []
train_labels = []
with open('./horseColicTraining.txt', 'r') as f:
for line in f.readlines():
current_line = line.strip().split('\t')
line_list = []
for i in range(21):
line_list.append(float(current_line[i]))
train_set.append(line_list)
train_labels.append(float(current_line[21]))
train_weights = stoc_grad_asent(np.array(train_set), train_labels, 500)
error_count = 0
num_test_vector = 0
with open('horseColicTest.txt', 'r') as f:
for line in f.readlines():
num_test_vector += 1
current_line = line.strip().split('\t')
line_list = []
for i in range(21):
line_list.append(float(current_line[i]))
if int(classify_vector(line_list, train_weights)) != int(current_line[21]):
error_count += 1
error_rate = float(error_count) / num_test_vector
print("the error rate of this test is: %f" % error_rate)
return error_rate
def main():
num_test = 10
error = [colic_test() for i in range(num_test)]
print("after %d iterations the average rate is: %f" % (num_test, np.mean(error)))
if __name__ == '__main__':
main()
输出结果为:
平均错误率为34%,这个结果并不差,因为有30%的数据缺失。
处理数据中缺失值
的可选做法是:
1、使用可用特征的均值来填补缺失的值
2、使用特殊值来填补缺失值,比如-1
3、忽略有缺失值的样本
4、使用相似样本的均值填补缺失
5、使用另外的机器学习算法预测缺失
对于本例子中缺失值的做法是:当缺失值是特征是用0代替,当缺失值是标签时,直接去除改样例。
参考文献
- Peter Harrington著,李锐,李鹏等译. 机器学习实战[M]. 人民邮电出版社.2018.1. p73-88