zoukankan      html  css  js  c++  java
  • 《机器学习十讲》第三讲总结

      源地址(相关案例在视频下方):http://cookdata.cn/auditorium/course_room/10014/

      《机器学习十讲》——第三讲:分类

      数学知识回顾:

    点到平面距离:

    梯度下降法:

    简介:求解无约束最优化问题的经典方法,机器学习和深度学习中应用最广泛的模型求解算法。

    定义:如果实值函数g(w)a处可微且有定义,那么函数g(w)a处沿着梯度相反的方向-▽g(a)下降最快

    优化问题:

    更新参数:

    随机梯度下降法:

    在机器学习中,优化目标和梯度具有特定结构:

    更新参数公式:

    随机梯度下降法绘制出来的图像是振动的,需要调整学习率来减小振幅,最终让它趋近于0:

    最大似然估计:

    介绍:一种求解概率模型的参数方法。

    概念:

      分类:

    介绍:分类是另一种典型的有监督学习问题。标签(模型预测值)y离散值

    做分类(附图示):

     线性可分训练集:

    感知机:找到一条直线,将两类数据分开

    支持向量机:找到一条直线,不仅将两类数据正确分类,还使得数据离直线尽量远。

    逻辑回归:找到一条直线使得观察到训练集的“可能性”最大。数据离直线越远越接近于1,反之则趋近于0

    训练集的矩阵表示(第二讲回顾):

      算法详细介绍:

      感知机:

    划出一条直线,将正负分开。当出现划分错误情况(如正值在左/负值在右),就需要进行优化,优化目标简单的说就是让划分错误的那个点尽可能地离直线近,即距离越小。

    优化目标介绍:

    优化目标就是L(w)函数。

    感知器算法——SGD:

      支持向量机:

    间隔最大化。

    样本损失函数:

    优化目标:

    非线性:核技巧

    映射trick:将数据点从二维空间映射到三维空间中,使得数据线性可分

    图示:

    原本映射到高维会出现维度灾难问题,使得计算量复杂化。而支持向量机的好处在于映射到高维的时候,计算量仍是低维空间的计算量。

      逻辑回归:

    赋予样本概率解释

    似然函数与负对数似然函数:

    优化目标就是NLL(w)最小值

      三种模型的损失函数对比:

    感知机是蓝色线,支持向量机是绿色线,逻辑回归是红色线

    分类问题的评价指标:

    负例0可变更为-1

    SKlearn分类模块介绍:

       案例——使用感知机、逻辑回归、支持向量机进行中文新闻主题分类

      首先是三种分类模型的实现介绍,数据集使用sklearn的datasets模块生成一个随机的二分类数据集

    #sklearn的make_classification方法:生成用于分类的随机数据集。
    from sklearn import datasets
    random_samples = datasets.make_classification(n_samples=60, #样本数量
                                                  n_classes=2, #类别数量
                                                  n_features=2, #特征数量
                                                  n_informative=2,#有信息特征数量
                                                  n_redundant=0, #冗余特征数量
                                                  n_repeated=0, # 重复特征数量
                                                  n_clusters_per_class=1, #每一类的簇数
                                                  flip_y=0, # 样本标签随机分配的比例
                                                  class_sep=3,#不同类别样本的分散程度
                                                  random_state=203)
    #将数据封装到Pandas的DataFrame结构中去
    import pandas as pd
    #两个特征:x1,x2
    data = pd.DataFrame(data=random_samples[0],columns=["x1","x2"])
    data["label"] = random_samples[1]
    data["ones"] = 1 #添加一个取值全为 1 的列 `ones`
    data.head()

    #为了直观体现,将标签取值为0的全部替换成-1
    data["label"] = data["label"].map({0:-1,1:1}) # 将 y 的取值替换成 1 和 -1 
    #标签取值替换好后根据标签值分离样本
    data_pos = data[data["label"]==1] # 筛选出正样本
    data_neg = data[data["label"]==-1] # 筛选出负样本
    #使用matplotlib绘图
    import matplotlib.pyplot as plt
    %matplotlib inline
    plt.rcParams['axes.unicode_minus'] = False
    
    plt.figure(figsize=(8, 8)) #设置图片尺寸
    
    plt.scatter(data_pos["x1"],data_pos["x2"],c="#E4007F",marker="^") # 类别为1的数据绘制成洋红色
    plt.scatter(data_neg["x1"],data_neg["x2"],c="#007979",marker="o") # 类别为-1的数据绘制成深绿色
    
    plt.xlabel("$x_1$") #设置横轴标签
    plt.ylabel("$x_2$") #设置纵轴标签
    
    plt.xlim(-6,6) #设置横轴显示范围
    plt.ylim(1,5) #设置纵轴显示范围
    plt.show()

     先在这个数据集上画一条直线看看效果:

    假设决策直线方程为

    #根据决策直线方程绘制直线
    import numpy as np
    w = [1,1,-4]
    x1 = np.linspace(-6, 6, 50)
    x2 = - (w[0]/w[1])*x1 - w[2]/w[1]
    plt.figure(figsize=(8, 8)) #设置图片尺寸
    
    plt.scatter(data_pos["x1"],data_pos["x2"],c="#E4007F",marker="^") # 类别为1的数据绘制成洋红色
    plt.scatter(data_neg["x1"],data_neg["x2"],c="#007979",marker="o") # 类别为-1的数据绘制成深绿色
    plt.plot(x1,x2,c="gray") # 画出分类直线
    
    plt.xlabel("$x_1$") #设置横轴标签
    plt.ylabel("$x_2$") #设置纵轴标签
    
    plt.xlim(-6,6) #设置横轴显示范围
    plt.ylim(1,5) #设置纵轴显示范围
    plt.show()

     接下来开始使用分类模型绘图:

    感知机模型:

    损失函数:,M为误分类样本集合。

    损失函数梯度:

    随机选取一个误分类样本,对参数W的更新方法:

     使用随机梯度下降法的感知机算法流程:

    #编写一个函数实现感知机算法
    def perception(X,y,learning_rate,max_iter=1000):
        w = pd.Series(data=np.zeros_like(X.iloc[0]),index=X.columns) # 初始化参数 w0
        W = [w] # 定义一个列表存放每次迭代的参数
        mis_samples = [] # 存放每次误分类的样本
        
        for t in range(max_iter):
            
            # 2.1 寻找误分类集合 M
            m = (X.dot(w))*y #yw^Tx < 0 的样本为误分类样本
            X_m = X[m <= 0]  # 误分类样本的特征数据
            y_m = y[m <= 0]  # 误分类样本的标签数据
            
            if(len(X_m) > 0): # 如果有误分类样本,则更新参数;如果不再有误分类样本,则训练完毕。
                # 2.2 从 M 中随机选取一个样本 i 
                i = np.random.randint(len(X_m))
                mis_samples.append(X_m.iloc[i,:])
                # 2.3 更新参数 w 
                w = w + learning_rate * y_m.iloc[i]*X_m.iloc[i,:]
                W.append(w)
            else: 
                break
                
        mis_samples.append(pd.Series(data=np.zeros_like(X.iloc[0]),index=X.columns))
        return w,W,mis_samples
    
    #使用刚才生成的数据集来进行测试
    ##函数使用:
    w_percept,W,mis_samples = perception(data[["x1","x2","ones"]], data["label"],1,max_iter=1000)
    
    #进行可视化,观察分类效果
    x1 = np.linspace(-6, 6, 50)
    x2 = - (w_percept[0]/w_percept[1])*x1 - w_percept[2]/w_percept[1]
    plt.figure(figsize=(8, 8)) #设置图片尺寸
    
    plt.scatter(data_pos["x1"],data_pos["x2"],c="#E4007F",marker="^") # 类别为1的数据绘制成洋红色
    plt.scatter(data_neg["x1"],data_neg["x2"],c="#007979",marker="o") # 类别为-1的数据绘制成深绿色
    plt.plot(x1,x2,c="gray") # 画出分类直线
    
    plt.xlabel("$x_1$") #设置横轴标签
    plt.ylabel("$x_2$") #设置纵轴标签
    plt.title('手动实现的感知机模型')
    plt.xlim(-6,6) #设置横轴显示范围
    plt.ylim(1,5) #设置纵轴显示范围
    plt.show()

     python可以通过一些函数实现动画模型,编写方法如下:

    #构建动画模型
    #plt.rcParams['figure.dpi'] = 120 #分辨率
    fig, ax = plt.subplots(figsize=(8, 8))
    line, = ax.plot([],[],c="gray")  ## 决策直线对象
    dot, = ax.plot([],[],"go", linewidth=2, markersize=12,markerfacecolor='none') ## 误分类样本对象
    
    def init_draw(): # 展现样本数据
        ax.set_title("感知机训练过程")
        ax.scatter(data_pos["x1"],data_pos["x2"],c="#E4007F",marker="^") # 类别为1的数据绘制成洋红色
        ax.scatter(data_neg["x1"],data_neg["x2"],c="#007979",marker="o") # 类别为-1的数据绘制成深绿色
        plt.xlabel("$x_1$")
        plt.ylabel("$x_2$")
        plt.xlim(-6,6)
        plt.ylim(1,5)
            
    def update_draw(i): # 实现动画中每一帧的绘制函数,i为第几帧
        ax.set_title("感知机训练过程 "+ str(i))
        w = W[i] #获取当前迭代的参数
        x1 = np.linspace(-6, 6, 50)
        x2 = - (w[0]/w[1])*x1 - w[2]/w[1] 
        line.set_data(x1,x2) #更新决策直线绘制
        dot.set_data(mis_samples[i]["x1"],mis_samples[i]["x2"]) # 更新选取的样本标记
        plt.close()
        
    #演示决策面动态变化
    import matplotlib.animation as animation
    from IPython.display import HTML
    animator = animation.FuncAnimation(fig, update_draw, frames= range(0,len(W)), init_func=init_draw,interval=2000)
    HTML(animator.to_jshtml())

    最后生成是这样的,不方便全部截图,因此只截一张:

    可以看到图中有个绿色点被画了圈,后续步骤就是根据这个标记点调整直线,最后的效果是上边的那个截图

    逻辑回归模型:

    逻辑回归的目标函数为负对数似然函数:,梯度为

     所以有两种实现方法:

    #逻辑回归
    ##梯度下降法
    import numpy as np
    # 定义梯度下降法求解的迭代公式
    def logistic_regression(X,y,learning_rate,max_iter=1000):
        # 初始化w
        w = np.zeros(X.shape[1])
        for t in range(max_iter):      
            # 计算yX
            yx = y.values.reshape((len(y),1)) * X 
            # 计算1 + e^(yXW)
            logywx = (1 +  np.power(np.e,X.dot(w)*y)).values.reshape(len(y),1) 
            w_grad = np.divide(yx,logywx).sum()
            # 迭代w
            w = w + learning_rate * w_grad    
        return w
    # 输出训练好的参数
    w = logistic_regression(data[["x1","x2","ones"]], data["label"],0.5,max_iter=1000)  
    print(w)
    
    # 可视化分类结果
    x1 = np.linspace(-6, 6, 50)
    x2 = - (w[0]/w[1])*x1 - w[2]/w[1]
    
    plt.figure(figsize=(8, 8))
    plt.scatter(data_pos["x1"],data_pos["x2"],c="#E4007F",marker="^") # 类别为1的数据绘制成洋红色
    plt.scatter(data_neg["x1"],data_neg["x2"],c="#007979",marker="o") # 类别为-1的数据绘制成深绿色
    plt.plot(x1,x2,c="gray")
    
    plt.xlabel("$x_1$")
    plt.ylabel("$x_2$")
    plt.xlim(-6,6)
    plt.ylim(1,5)
    plt.show()

    ##逻辑回归——随机梯度下降法
    # 定义随机梯度下降法求解的迭代公式
    def logistic_regression_sgd(X,y, learning_rate, max_iter=1000): 
        # 初始化w
        w = np.zeros(X.shape[1])
        for t in range(max_iter):
            # 随机选择一个样本
            i = np.random.randint(len(X))
            # 计算yx
            yixi = y[i] * X.values[i]
             # 计算1 + e^(yxW)
            logyiwxi = 1 +  np.power(np.e, w.T.dot(X.values[i])*y[i])
            w_grad = yixi / logyiwxi
            
            # 迭代w
            w = w + learning_rate * w_grad  
            
        return w
    # 输出训练好的参数
    w = logistic_regression_sgd(data[["x1","x2","ones"]], data["label"],0.5,max_iter=1000)  
    print(w)
    
    # 可视化分类结果
    x1 = np.linspace(-6, 6, 50)
    x2 = - (w[0]/w[1])*x1 - w[2]/w[1]
    
    plt.figure(figsize=(8, 8))
    plt.scatter(data_pos["x1"],data_pos["x2"],c="#E4007F",marker="^") # 类别为1的数据绘制成洋红色
    plt.scatter(data_neg["x1"],data_neg["x2"],c="#007979",marker="o") # 类别为-1的数据绘制成深绿色
    plt.plot(x1,x2,c="gray")
    
    plt.xlabel("$x_1$")
    plt.ylabel("$x_2$")
    plt.xlim(-6,6)
    plt.ylim(1,5)
    plt.show()

     支持向量机模型:

    #支持向量机
    import numpy as np
    import matplotlib.pyplot as plt
    %matplotlib inline
    
    # 定义函数
    def linear_svm(X,y,lam,max_iter=2000):  
        w = np.zeros(X.shape[1]) # 初始化w
        support_vectors = [] # 创建空列表保存支持向量
        
        for t in range(max_iter): # 进行迭代
            
            learning_rate = 1/(lam * (t + 1)) # 计算本轮迭代的学习率
            i = np.random.randint(len(X)) # 从训练集中随机抽取一个样本
            ywx = w.T.dot(X.values[i])*y[i]  # 计算y_i w^T x_i
            
            if ywx < 1:# 进行指示函数的判断
                w = w - learning_rate * lam*w + learning_rate * y[i] * X.values[i] # 更新参数       
            else:
                w = w - learning_rate * lam*w # 更新参数
        for i in range(len(X)):
            ywx = w.T.dot(X.values[i])*y[i]  # 计算y_i w^T x_i
            if ywx <= 1: # 根据样本是否位于间隔附近判断是否为支持向量
                support_vectors.append(X.values[i])
        
        return w,support_vectors
    ##由于线性支持向量机的正则化不包括截距项,因此需要进行归一化
    # 对训练集数据进行归一化,则模型无需再计算截距项
    X = data[["x1","x2"]].apply(lambda x: x - x.mean())
    # 训练集标签
    y = data["label"]
    w,support_vectors = linear_svm(X,y, lam=0.05, max_iter=5000)
    # 创建绘图框
    plt.figure(figsize=(8, 8))
    # 绘制两类样本点
    X_pos = X[ y==1 ]
    X_neg = X[ y==-1 ]
    plt.scatter(X_pos["x1"],X_pos["x2"],c="#E4007F",marker="^") # 类别为1的数据绘制成洋红色
    plt.scatter(X_neg["x1"],X_neg["x2"],c="#007979",marker="o") # 类别为-1的数据绘制成深绿色
    
    # 绘制超平面
    x1 = np.linspace(-6, 6, 50)
    x2 = - w[0]*x1/w[1]
    plt.plot(x1,x2,c="gray")
    
    # 绘制两个间隔超平面
    plt.plot(x1,-(w[0]*x1+1)/w[1],"--",c="#007979")
    plt.plot(x1,-(w[0]*x1-1)/w[1],"--",c="#E4007F")
    
    # 标注支持向量
    for x in support_vectors:
        plt.plot(x[0],x[1],"ro", linewidth=2, markersize=12,markerfacecolor='none')
        
    # 添加轴标签和限制轴范围
    plt.xlabel("$x_1$")
    plt.ylabel("$x_2$")
    plt.xlim(-6,6)
    plt.ylim(-2,2)

       简单过了一遍算法之后,我们开始进行中文新闻分类:

    #读取新闻数据并显示前五行
    raw_train = pd.read_csv("./input/chinese_news_cutted_train_utf8.csv",sep="	",encoding="utf8")
    raw_test = pd.read_csv("./input/chinese_news_cutted_test_utf8.csv",sep="	",encoding="utf8")
    raw_train.head()

    #这里仅进行二分类,选择主题为科技和文化的新闻
    raw_train_binary = raw_train[((raw_train["分类"] == "科技") | (raw_train["分类"] == "文化"))]
    raw_test_binary = raw_test[((raw_test["分类"] == "科技") | (raw_test["分类"] == "文化"))]
    raw_test_binary.head()

    #先加载停用词表,并使用该表去除文本中的停用词
    stop_words = []
    file = open("./input/stopwords.txt") 
    for line in file:
        stop_words.append(line.strip())
    file.close()
    #之后将文本数据转换为词向量(第二讲内容)
    ##调用sklearn.feature_extraction.text的CountVectorizer
    from sklearn.feature_extraction.text import CountVectorizer
    vectorizer = CountVectorizer(stop_words=stop_words)
    X_train = vectorizer.fit_transform(raw_train_binary["分词文章"])
    X_test = vectorizer.transform(raw_test_binary["分词文章"])
    #再调用sklearn中的随机梯度下降分类器——SGDClassifier
    ##调整loss参数分别构建感知机,逻辑回归和线性支持向量机模型
    from sklearn.linear_model import SGDClassifier
    ###感知机模型——loss="perceptron"
    percep_clf = SGDClassifier(loss="perceptron",penalty=None,learning_rate="constant",eta0=1.0,max_iter=1000,random_state=111)
    ###逻辑回归模型——loss="log"
    lr_clf = SGDClassifier(loss="log",penalty=None,learning_rate="constant",eta0=1.0,max_iter=1000,random_state=111)
    ###线性支持向量机模型——loss="hinge"
    lsvm_clf = SGDClassifier(loss="hinge",penalty="l2",alpha=0.0001,learning_rate="constant",eta0=1.0,max_iter=1000,random_state=111)

    之后就是训练各个模型并得出正确率,再进行比对:

    #训练感知机模型并输出分类正确率
    percep_clf.fit(X_train,raw_train_binary["分类"])
    round(percep_clf.score(X_test,raw_test_binary["分类"]),2)

    #训练逻辑回归模型并输出分类正确率
    lr_clf.fit(X_train,raw_train_binary["分类"])
    round(lr_clf.score(X_test,raw_test_binary["分类"]),2)

    #训练线性支持向量机模型并输出分类正确率
    lsvm_clf.fit(X_train,raw_train_binary["分类"])
    round(lsvm_clf.score(X_test,raw_test_binary["分类"]),2)

     可以看出线性支持向量机的正确率要高一些

    最后进行一下模型评估,依然是图表格式:

    #模型效果评估
    from sklearn.metrics import confusion_matrix
    import seaborn as sns
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots(figsize=(5,5))
    # 设置正常显示中文
    sns.set(font='SimHei')
    # 绘制热力图
    y_svm_pred = lsvm_clf.predict(X_test) # 预测标签
    y_test_true = raw_test_binary["分类"] #真实标签
    confusion_matrix = confusion_matrix(y_svm_pred,y_test_true)#计算混淆矩阵
    ax = sns.heatmap(confusion_matrix,linewidths=.5,cmap="Blues",
                     annot=True, fmt='d',xticklabels=lsvm_clf.classes_, yticklabels=lsvm_clf.classes_)
    ax.set_ylabel('真实')
    ax.set_xlabel('预测')
    ax.xaxis.set_label_position('top') 
    ax.xaxis.tick_top()
    ax.set_title('混淆矩阵热力图')

    #绘制三种分类模型的损失函数曲线
    import numpy as np
    import matplotlib.pyplot as plt
    %matplotlib inline
    yfx = np.linspace(-4, 4, 500)
    perception = [0 if i >= 0 else -i for i in yfx]
    hinge = [(1-i) if i <= 1 else 0 for i in yfx]
    log = np.log2(1 + np.power(np.e,-yfx))
    plt.figure(figsize=(8, 6))
    plt.plot(yfx,perception,c="b",label="感知机损失")
    plt.plot(yfx,hinge,c="g",label="合页损失(SVM)")
    plt.plot(yfx,log,c="red",label="对数损失(LR)")
    plt.hlines(1,-4,0)
    plt.vlines(0,0,1)
    plt.xlabel("$yf(x)$")
    plt.ylabel("$L_i(y_i,yf(x))$")
    plt.xlim(-4,4)
    plt.ylim(0,6)
    plt.legend()

    #绘制三种分类模型的从回归到分类的映射函数
    import numpy as np
    import matplotlib.pyplot as plt
    %matplotlib inline
    #创建画布并引入axisartist工具。
    import mpl_toolkits.axisartist as axisartist
    #创建画布
    fig = plt.figure(figsize=(8, 8))
    fx = np.linspace(-10, 10, 500)
    step = [1 if i >= 0 else -1 for i in fx]
    tanh = np.tanh(fx)
    sigmoid = 1/(1 + np.power(np.e,-fx))
    plt.axhline(0,-10,10,color="k")
    plt.axvline(0,-2,2,color="k")
    plt.plot(fx,step,c="b",label="step")
    plt.plot(fx,tanh,c="g",label="tanh")
    plt.plot(fx,sigmoid,c="red",label="sigmoid")
    plt.xlabel("$f$")
    plt.ylabel("$H(f)$")
    plt.grid(False)
    plt.xlim(-10, 10)
    plt.ylim(-2,2)
    plt.axis('off')
    plt.legend()

  • 相关阅读:
    RabbitMQ 消息可靠性
    SpringBoot2.X+SpringAMQP 整合 RabbitMQ
    《红宝书》 |Array数组介绍及用法
    《红宝书》 |单例内置对象 |Global和Math
    js封装 |随机获取指定范围内的整数
    《红宝书》 |原始包装类型
    《红宝书》 |什么是对象
    js封装 |时间对象相关方法
    兼容 |ios移动端的时间对象
    《红宝书》 |基本引用类型-正则表达式RegExp
  • 原文地址:https://www.cnblogs.com/20183711PYD/p/14341590.html
Copyright © 2011-2022 走看看