机器学习笔记(三):线性回归大解剖(代码部分)

这里,让我手把手教你如何用逻辑回归分析数据

根据学生分数预测是否录取:

#必备3个库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

让我们读入数据:

import os
path = "data" + os.sep + "LogiReg_data.txt";   # os.sep 表示的是文件分割符 “ / ” 因为win和linux分隔符不同所以 为了跨平台就不直接写

pdData = pd.read_csv(path,header=None,names=['Exam1','Exam2','Admitted']);  #header默认使用第一行做标题行,这里改为自己设置
pdData.shape;    #数据的纬度  (100 , 3)  
pdData.head()    #不加参数表示默认显示5行

前两列数据表示分数,最后一列表示是否录取:

机器学习笔记(三):线性回归大解剖(代码部分)

让我们简单看一下,录取和没录取在不同分数上的显示情况

positive = pdData[pdData['Admitted' ] == 1]        #  录取的集合
nagative = pdData[pdData['Admitted' ] == 0]      #  没录取集合

fig, ax=plt.subplots(figsize=(10,5))   # ax前面必须有fig  figsize设置子图的宽和高

#分1 做x  分2做y 
ax.scatter(positive['Exam1'],positive['Exam2'],s=30,c='blue',marker='o',label='Admitted') #s=点大小  c='颜色' marker=标记
ax.scatter(nagative['Exam1'],nagative['Exam2'],s=30,c='r',marker='x',label='Not Admitted')
ax.legend()

ax.set_xlabel('Exam1 score')
ax.set_ylabel('Exam2 score'
plt.show()

可以看到有明显的分界线:机器学习笔记(三):线性回归大解剖(代码部分)

接下来就是正式的分析了
我们的目标是:建立分类器 ,求出3个参数(分数1的参数,分数2的参数,误差)
设定阈值,根据阈值判断是否录取(这里>=0.5就当作录取)

要完成的模块:
#1.sigmoid:映射到概率(0-1)的函数 g(z) = 1/(1+e^-z)
#2.model: 返回预测结果值
#3.cost: 根据参数计算损失
#4.gradient: 计算每个参数的梯度方向
#5.descent: 进行参数更新
#6.accuracy: 计算精度

1.sigmoid 函数

值域(0-1) g(-∞) = 0 g(+∞) = 1 g(0)=0.5

def sigmoid(z):
    return 1/(1+np.exp(-z))

2.model

就是用数据作为sigmoid的输入,从而构造出预测函
数据 (1,x1,x2) * (θ0 , θ1 , θ2)的转置 = θ0+θ1X1+θ2X2
然后传到 sigmoid中得到结果

def model(X,theta):
    return sigmoid( np.dot(X,theta.T) )   
#结果中 θ0需要和1相乘,数据中缺少1这1列,所以要补进
pdData.insert(0,'Ones',1); #在第0列(第一列之前,索引之后 插入列名为‘ones’,所有数据为1的一列
data = pdData.as_matrix()  #将pdData从DataFrame 转为矩阵,方便进行矩阵乘法
cols = data.shape[1] 	#数据有几列  4   ones  exam1  exam2  admitte
X = data[:,0:cols-1]  #数据矩阵X  1,分数1,分数2
y = data[:,cols-1:cols];  # 结果(是否选上
theta=np.zeros([1,3]) # 3个参数   参数个数=数据个数+1

3.损失函数

这里就是对数似然函数 * -1/m [加-是因为要把梯度上什转为梯度下降,1/m是为了求平均] 似然函数 = 每个 x 对应的 P(y|x;θ) 的连乘 , 然后取对数得到对数似然函数

损失函数(loss function)是用来衡量模型的预测值f(x)与真实值Y的不一致程度,它是一个非负实值函数,损失函数越小,模型越优(还需考虑过拟合等问题)

这里选择用 似然函数*-1/m 作为损失函数,是不是因为 似然函数越大越好 , 加一个 负号就越小越好 , 所以只要平均似然函数的值越来越小了,就说明越来越好了?

损失函数用: J(θ) = -1/m * sum( (y*log(hθ(X)) + (1-y)log(1-hθ(x)) ) )

def cost(X,y,theta):
    tt = y-model(X,theta)
    left = np.multiply(tt,tt)
    return np.sum(left)/(len(X))

用 1/m * sum( (y - hθ(X) )^2 ) 做损失函数 效果相同

def cost(X,y,theta):
     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)

4.求梯度

多维图像的梯度,也就是多维图像在某点各个方向的斜率中斜率最大的那个

算法就是对J(θ) 求偏导 dJ(θ)/dθ = -1/m * sum( (yi - hθ(xi))xij )

def gradient(X,y,theta):
    grad = np.zeros(theta.shape) #有几个θ,就找几个梯度
    
     # dJ(θ)/dθ = -1/m * sum( (yi - hθ(xi))xij )
    error = (model(X,theta) - y).ravel();  #  yi - hθ(xi) 将-号提取进来   ravel将多维降为一维矩阵 ,因为原error是列向量,所以作用和专T类似 
    #求每个θi的梯度
    for j in range (theta.shape[1]):
        term = np.multiply(error,X[:,j])  #Xij   表示的是 X的每一个样本,针对每一个参数θj 都只取和θj有关的那一列即X( , j)行不限
        grad[0,j] = np.sum(term) / len(X)
    return grad

4 (附属) 停止策略

梯度更新需要有个头,有个结束点

#设计了3种不同的停止策略
STOP_ITER = 0 #按照迭代次数停止 (一次迭代即更新一次梯度值)
STOP_COST = 1 #目标函数两次迭代之间的损失(差异)很小   
STOP_GRAD = 2 #两次迭代之间梯度没什么变化


def stopCriterion(type,value,threshold):  
    #设定3种不同的停止策略
    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;   #求2范数  sqrt(x1^2+x2^2+.....)

4 (附属) 对数据进行洗牌

打乱(防止自己收集数据的时候按照某种规律收集)

def shuffleData(data):
    np.random.shuffle(data)  #洗牌数据
    #重新获取数据
    cols = data.shape[1] #数据有几列  4   ones  exam1  exam2  admitted
    X = data[:,0:cols-1]  #数据矩阵X  1,分数1,分数2
    y = data[:,cols-1:cols];  # 结果(是否选上)
    return X,y

5,6重点!

查看不同梯度下降策略对时间的影响

import time
def descent(data, theta, batchSize, stopType ,thresh, alpha):  #batchSize  =  1 随机梯度下降    =样本个数 梯度下降   =1-样本个数  小批量样本夏婧
    init_time = time.time();#开始时候的时间                 #thresh  阈值   alpha学习率
    i = 0;#迭代次数   
    k = 0;#batch ,即当前已经消耗的样本数量
    X,y = shuffleData(data)
    grad = np.zeros(theta.shape) #梯度
    costs = [cost(X,y,theta)] #损失值
    
    #***5.descent: 进行参数更新***
    while True:
        grad = gradient(X[k:k+batchSize],y[k:k+batchSize],theta)
        #更新参数 , 让θ 不停的靠近梯度为0的点
        theta = theta - alpha*grad;
        
        costs.append(cost(X,y,theta))  #用新的 θ 计算出新的损失值
        i += 1; #迭代次数 +1
    
        k+=batchSize 
        if k >= len(X):    
            k=0;
            X,y = shuffleData(data);  #重新洗牌
        
        value = 0;
        if stopType == STOP_ITER:  value = i;
        elif stopType == STOP_COST: value = costs;
        elif stopType == STOP_GRAD: value = grad;
        if stopCriterion(stopType,value,thresh):break;  #是否跳出
        

    return theta,i-1,costs,grad,time.time()-init_time

功能函数,画图

#功能函数,画图
def runExpe(data, theta, batchSize, stopType, thresh, alpha):
    #import pdb; pdb.set_trace();
    theta, iter, costs, grad, dur = descent(data, theta, batchSize, stopType, thresh, alpha)
    name = "Original" if (data[:,1]>2).sum() > 1 else "Scaled"
    name += " data - learning rate: {} - ".format(alpha)
    if batchSize==n: strDescType = "Gradient"
    elif batchSize==1:  strDescType = "Stochastic"
    else: strDescType = "Mini-batch ({})".format(batchSize)
    name += strDescType + " descent - Stop: "
    if stopType == STOP_ITER: strStop = "{} iterations".format(thresh)
    elif stopType == STOP_COST: strStop = "costs change < {}".format(thresh)
    else: strStop = "gradient norm < {}".format(thresh)
    name += strStop
    print ("***{}\nTheta: {} - Iter: {} - Last cost: {:03.2f} - Duration: {:03.2f}s".format(
        name, theta, iter, costs[-1], dur))
    fig, ax = plt.subplots(figsize=(12,8))
    ax.plot(np.arange(len(costs)), costs, 'r')
    ax.set_xlabel('Iterations')
    ax.set_ylabel('Cost')
    ax.set_title(name.upper() + ' - Error vs. Iteration')
    plt.show()
    return theta

数据标准化

((a-均值)/方差)

from sklearn import preprocessing as pp
scaled_data = data.copy()
scaled_data[:, 1:3] = pp.scale(data[:, 1:3])
n=100   
#损失值 < 0.000001时停
theta = runExpe(scaled_data, theta, n, STOP_COST, thresh=0.000001, alpha=0.001)

6.accuracy: 计算精度


def predict(X,theda):  #设定阈值   预测 大于0.5就是可以
    return [1 if x>=0.5 else 0 for x in model(X,theta)]

scaled_X = scaled_data[:, :3]
y = scaled_data[:, 3]
predictions = predict(scaled_X, theta)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]

accuracy = (sum(map(int, correct)) % len(correct))
print ('accuracy = {0}%'.format(accuracy))

机器学习笔记(三):线性回归大解剖(代码部分)