NLP-Beginner任务一学习笔记:基于机器学习的文本分类

本文介绍了使用NumPy实现基于Logistic回归和SoftmaxRegression的文本分类,探讨了Bag-of-Words(BOW)和N-gram的特征表示方法,以及批量、随机和小批量梯度下降法的优缺点。实验部分对比了不同特征提取和梯度下降方法在训练集和测试集上的性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

**实现基于logistic/softmax regression的文本分类**

数据集:Classify the sentiment of sentences from the Rotten Tomatoes dataset

实现要求:NumPy

目录

一、知识点学习 

(一)文本的特征表示

1、Bag of Word(BOW模型)

代码实现

2、N-gram

代码实现

(二)分类器

1、Logistic回归

代码实现

2、Softmax Regression

代码实现

(三)损失函数

(四)梯度下降

1、批量梯度下降法(batch)

2、随机梯度下降法(shuffle)

3、小批量梯度下降法(mini-batch)

4、三者优缺点比较

(五)训练集/验证集/测试集的划分

二、实验 

(一)代码

(二)结果分析


一、知识点学习 

(一)文本的特征表示

数据集描述

1、Bag of Word(BOW模型)

面对庞大而又非结构化的文本数据,必须将其转换成向量才能够输入机器学习算法中,即特征提取(特征编码)过程。词袋模型BOW就是其中一种经典方法。

词袋模型(Bag-of-Words model,BOW)最初被用在文本分类中,将文档表示成特征矢量

其基本思想是假定对于一个文本,忽略其词序和语法、句法,仅仅将其看做是一些词汇的集合,而文本中的每个词汇都是独立的。具体而言,是把一个句子拆成多个单词,存在于句子的单词则对应的向量位置上的数字为1,反之为0,若一个词多次出现,则将得到的文档词频作为一个 feature。

举例:

句子:“我喜欢你”  “我不喜欢你”  “我知道你不喜欢我”

词袋:{“我”:1,“喜欢”:2,“你”:3,“不”:4,“知道”:5}

# 在上述词典中,key为词,value为词的索引,词袋共有5个单词, 因此每个文本可以用一个5维向量来表示,即上述文本可以表示为:

句子:[1,1,1,0,0]  [1,1,1,1,0]  [2,1,1,1,1]

BOW模型的作用和缺点:

1、作用:将两个文档通过词袋模型变为句子向量,通过计算向量的余弦距离来计算两个文档间的相似度,从而便于实现文本分类。

2、缺点:词库庞大难以维护;忽略词序语法会造成原本完全不同的两句话向量相同;无法规避常用词;向量稀疏等等。

代码实现

"""定义Bagofwords类"""
class Bagofwords:
    # 定义类的初始化函数
    def __init__(self, data, max_item=20000):
        # 将输入的数据存储在成员变量data中,只保留前max_item条数据。
        self.data = data[:max_item]
        self.max_item = max_item
        # 定义一个空字典,作为特征表
        self.dict_words = dict()
        # 初始化成员变量len为0,用于记录特征表中不同单词的数量。
        self.len = 0
        # 将输入的数据data按照测试集比例0.2进行切分,将切分后的训练集和测试集分别存储在成员变量train和test中。
        self.train, self.test = data_split(data, test_rate=0.2, max_item=max_item)
        # 将训练集中每个样本的类别(term[3]是sentiment值,数据标签)存储在成员变量train_y中,将类别转换为整数。
        self.train_y = [int(term[3]) for term in self.train]
        self.test_y = [int(term[3]) for term in self.test]
        # 初始化成员变量train_matrix和test_matrix(特征向量)为None。
        self.train_matrix = None
        self.test_matrix = None

    # 定义函数,用于生成特征表。
    def get_words(self):
        # 遍历输入数据中的每个样本,将每个样本的文本内容转换为大写,并使用split函数将文本切分为单词列表。
        for term in self.data:
            s = term[2]   # 评论文本
            s = s.upper()
            words = s.split()
            # 遍历单词列表中的每个单词,如果这个单词不在特征表dict_words中,则将其加入到特征表中,并将其编号为当前特征表中最大编号加1。
            for word in words:
                if word not in self.dict_words:
                    # 赋予新的索引len(self.dict_words)
                    self.dict_words[word] = len(self.dict_words)
        # 将特征表dict_words中不同单词的数量存储在成员变量len中,初始化特征向量矩阵train_matrix。
        self.len = len(self.dict_words)
        self.test_matrix = numpy.zeros((len(self.test), self.len))
        self.train_matrix = numpy.zeros((len(self.train), self.len))

    # 该函数返回了经过BOW模型转化后的训练集和测试集的矩阵表示。
    def get_matrix(self):
        for i in range(len(self.train)):
            s = self.train[i][2]
            words = s.split()
            # 通过对训练集中的每一条数据进行遍历,依次对每个单词进行处理。
            for word in words:
                # 将单词转换成大写形式并在词典中查找其对应的索引,然后将对应位置上的数值标记为1。
                word = word.upper()
                self.train_matrix[i][self.dict_words[word]] = 1
        for i in range(len(self.test)):
            s = self.test[i][2]
            words = s.split()
            for word in words:
                word = word.upper()
                self.test_matrix[i][self.dict_words[word]] = 1

"""调用BOW模型"""
# 特征值提取
bag = Bagofwords(data)
#获取输入数据中出现的所有单词
bag.get_words()
# 将输入数据转换成基于词袋模型的特征矩阵
bag.get_matrix()

2、N-gram

相较于BOW词袋模型,N-gram考虑了词序。N-gram模型是一种基于概率的判别模型,它的输入是一句话(单词的顺序序列),输出是这句话的概率,即这些单词的联合概率。

N-gram本身也指一个由N个单词组成的集合,各单词具有先后顺序,且不要求单词之间互不相同。

N-gram假设第n个词出现与前n-1个词相关,而与其他任何词不相关。整个句子出现的概率等于各个词出现的概率乘积,各个词的概率可以通过语料中统计计算得到。N的取值表示下一个词的出现依赖它前面的N个词。

上述词袋模型仅考虑了单词存在与否,而N-gram考虑了词组的存在与否。例如,当N=2时,“我喜欢你” 不再看作是 “我”, “喜欢”, “你” 这三个单词,而是 “我喜欢”, “喜欢你” 这两个词组。

理论上,n 越大越好,但随着N的递增,文本的特征数也会上涨,因此遇到数据量大的输入时,较大的N会导致结果模型训练极度缓慢。因此,能用 bigram 解决,绝不使用 trigram。

假设句子T由词序列w1,w2,w3...wn组成,用公式表示N-Gram语言模型如下:

P(T)=p(w1)*p(w2)*p(w3)...p(wn)=p(w1)*p(w2|w1)*p(w3|w1w2)...p(wn|w1w2w3...)

N-gram的优缺点:

优点:包含了前N-1个词所能提供的全部信息,这些词对于当前词的出现具有很强的约束力。

缺点:需要相当规模的训练文本来确定模型的参数;因数据稀疏而导致的数据平滑问题。N-gram模型是根据相互之间没有任何遗传属性的离散单元词而构建,从而不具备连续空间中的词向量所满足的语义上的优势:相似意义的词语具有相似的词向量,从而当系统模型针对某一词语或词序列调整参数时,相似意义的词语和词序列也会发生改变。

因此,如果在已知关键词权重非常大的情况下,使用N-gram模型或许比较合适。

代码实现

"""定义NGram类"""
class NGram:
    def __init__(self, data, dimension=2, max_item=20000):
        self.data = data[:max_item]
        self.max_item = max_item
        self.dict_words = dict()
        # 初始化N - Gram特征词典长度为0
        self.len = 0
        # 表示n - gram的最大维度
        self.dimension = dimension
        self.train, self.test = data_split(data, test_rate=0.2, max_item=max_item)
        self.train_y = [int(term[3]) for term in self.train]  # 提取样本标签
        self.test_y = [int(term[3]) for term in self.test]
        self.train_matrix = None  # 初始化训练集和测试集特征矩阵为空。
        self.test_matrix = None

    # 特征词提取函数
    def get_words(self):
        # 循环从1到self.dimension,提取1-gram, 2-gram,..., dimension-gram的特征。
        for d in range(1, self.dimension + 1):
            # 循环遍历self.data中的每一个元素,将元素赋值给变量term。
            for term in self.data:
                s = term[2]   # s为数据集中的评论文本
                s = s.upper()   # 将评论文本均转换成大写
                words = s.split()
                # 对于每个单词,从该单词开始提取连续的d个单词,d - gram词汇表示为temp。
                for i in range(len(words) - d + 1):
                    # 将d个单词连接在一起,中间使用下划线'_'连接,构建出d - gram特征。
                    temp = words[i:i + d]
                    temp = "_".join(temp)
                    # 如果构建出的d - gram特征词不在词典中,将其加入并赋予索引。
                    if temp not in self.dict_words:
                        self.dict_words[temp] = len(self.dict_words)
        self.len = len(self.dict_words)
        self.test_matrix = numpy.zeros((len(self.test), self.len))
        self.train_matrix = numpy.zeros((len(self.train), self.len))

    def get_matrix(self):

        for d in range(1, self.dimension + 1):
            for i in range(len(self.train)):
                s = self.train[i][2]  # 评论文本
                s = s.upper()  # 评论文本转化成大写
                words = s.split()
                for j in range(len(words) - d + 1):
                    # 将单词列表中从第j个单词开始的长度为d的子列表(ngram特征)赋值给temp。
                    temp = words[j:j + d]
                    # 使用"_".join(temp)将temp列表中的单词用下划线连接成一个字符串。
                    temp = "_".join(temp)
                    # 将生成的字符串temp在self.dict_words中查找对应的索引,并将矩阵中对应的位置设为1
                    self.train_matrix[i][self.dict_words[temp]] = 1
            for i in range(len(self.test)):
                s = self.test[i][2]
                s = s.upper()
                words = s.split()
                for j in range(len(words) - d + 1):
                    temp = words[j:j + d]
                    temp = "_".join(temp)
                    self.test_matrix[i][self.dict_words[temp]] = 1

"""调用NGram"""
gram = NGram(data, dimension=2)
#获取输入数据中出现的所有单词
gram.get_words()
# 将输入数据转换成基于N-gram模型的特征矩阵。
gram.get_matrix()

(二)分类器

1、Logistic回归

Logistic 回归(Logistic Regression,LR)是一种常用的处理二分类问题的线性模型,其采用交叉熵作为损失函数,并使用梯度下降法来对参数进行优化。 

 

其中,logistic函数表现形式为:

代码实现

import pandas as pd
import numpy
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn import metrics
from sklearn import linear_model
from feature_extraction import Bagofwords, NGram

df = pd.read_csv("data/train.csv")
x_train, x_test, y_train, y_test = train_test_split(df['Phrase'], df['Sentiment'], test_size=0.2)

tf = TfidfVectorizer()
x_train = tf.fit_transform(x_train)
x_test = tf.transform(x_test)

clf = linear_model.LogisticRegression(C=1.0, penalty='l2', tol=0.01, solver="sag").fit(x_train, y_train)
predicted = clf.predict(x_test)
print(metrics.classification_report(y_test, predicted))
print('accuracy_score: %0.5fs' %(metrics.accuracy_score(y_test, predicted)))

返回结果:

2、Softmax Regression

举例:

在该数据集的Softmax回归中,给定一个输入x,输出一个y向量,y的长度即为类别的个数,总和为1,y中的数字是各类别的概率。

例如:(0~4共五类)下式代表输入x,其是类别2的概率为0.7,类别3的概率为0.25...

y=(0,0,0.7,0.25,0.05) ^\mathrm{T}

假设给定C个类别,softmax公式为:

\widehat{y}=f(x)=\operatorname{softmax}\left(W^{T} x\right)=\frac{\exp \left(W^{T} x\right)}{\sum_{c'=1}^{C} \exp \left(W^{T} x\right)}

W=[w1,w2,...,wC]为权重矩阵,\widehat{y}的每一个元素,代表对应类别的概率。因此,接下来便是给定𝑁个训练样本,Softmax回归使用交叉熵损失函数来学习最优的参数矩阵W。

代码实现

import numpy
import random

"""Softmax Regression"""
class Softmax:
    # 分别表示训练集中样本数量、分类数量和特征向量的大小
    def __init__(self, sample, typenum, feature):
        self.sample = sample
        self.typenum = typenum
        self.feature = feature
        # 使用numpy.random.randn方法对权重矩阵self.W进行初始化,其大小为feature行typenum列
        self.W = numpy.random.randn(feature, typenum)  # Weight matrix initialization

    """计算向量的softmax函数值"""
    def softmax_calculation(self, x):
        # 将向量x减去其最大值,以避免指数函数计算时出现数值溢出
        exp = numpy.exp(x - numpy.max(x))
        # 使用 numpy.exp 方法计算每个元素的指数值,并将其保存在 exp 中。最后将 exp 中每个元素除以其总和,得到 softmax 函数值
        return exp / exp.sum()

    """计算矩阵的softmax函数值"""
    # wtx是一个矩阵
    def softmax_all(self, wtx):
        # 将矩阵wtx中的每行元素都减去其最大值,以避免指数函数计算时出现数值溢出。
        wtx -= numpy.max(wtx, axis=1, keepdims=True)
        # 使用numpy.exp方法计算矩阵wtx中每个元素的指数值,并将其保存在wtx中。
        wtx = numpy.exp(wtx)
        # 最后将wtx中每行元素除以该行元素总和,得到 softmax函数值。
        wtx /= numpy.sum(wtx, axis=1, keepdims=True)  # 对矩阵的每一行进行操作,keepdims=True保持计算结果的维度不变,保持每行元素都计算 softmax 函数值后仍然是一行
        return wtx

    """将整数y转换为one-hot向量"""
    # 该函数将一个整数表示的类别转换为一个向量表示的类别。
    def change_y(self, y):
        # 其中self.typenum表示向量的维度,ans是一个长度为typenum的零向量,将第y个位置赋值为1
        ans = numpy.array([0] * self.typenum)
        ans[y] = 1
        return ans.reshape(-1, 1)

    """将整数y转换为one-hot向量"""
    # 该函数给定输入数据X,对其进行分类并返回预测结果。
    def prediction(self, X):
        # 其中X.dot(self.W)计算了输入数据X与模型参数W的乘积
        # 使用softmax函数对结果进行归一化处理得到各个分类的概率分布,最后返回概率最大的分类。
        prob = self.softmax_all(X.dot(self.W))
        return prob.argmax(axis=1)

    """计算分类器在训练集和测试集上的分类准确率"""
    # train_y 和 test_y 分别是训练集和测试集的真实分类标签。
    def correct_rate(self, train, train_y, test, test_y):
        # 首先计算训练集和测试集上的预测结果 pred_train 和 pred_test。
        n_train = len(train)
        pred_train = self.prediction(train)
        # 计算预测结果与真实标签相同的样本数占总样本数的比例,即为分类准确率。
        train_correct = sum([train_y[i] == pred_train[i] for i in range(n_train)]) / n_train
        n_test = len(test)
        pred_test = self.prediction(test)
        test_correct = sum([test_y[i] == pred_test[i] for i in range(n_test)]) / n_test
        #分别输出训练集和测试集上的分类准确率
        print(train_correct, test_correct)
        return train_correct, test_correct

    """Softmax回归"""
    # Python类中的方法,执行softmax回归
    # X:numpy 数组,表示输入数据,每行代表一个数据样本,每列代表一个特征。
    # y:numpy 数组,表示输出数据,它的每个元素与 X 中的一行对应。
    # alpha:学习率,用于指定每次更新参数时的步长。
    # times:训练次数,即迭代次数。
    # strategy:用于指定使用哪种训练策略,有 "mini"、"shuffle" 和 "batch" 三种可选,分别表示小批量梯度下降、随机梯度下降和批量梯度下降。
    def regression(self, X, y, alpha, times, strategy="mini", mini_size=100):
        # 判断输入的样本数量与输出的数量是否相同,如果不相同,就抛出异常。
        if self.sample != len(X) or self.sample != len(y):
            raise Exception("Sample size does not match!")
        # 当训练策略为小批量梯度下降时执行的代码
        if strategy == "mini":
            # 训练次数循环
            for i in range(times):
                increment = numpy.zeros((self.feature, self.typenum))
                # 每次训练时随机选择小批量数据进行训练
                for j in range(mini_size):
                    k = random.randint(0, self.sample - 1)
                    # -1表示这个维度的长度需要根据其他维度的长度自动推导出来,而1表示这个维度的长度为1
                    # 返回typenum行、1列的二维数组。转置后typenum行feature列*feature行1列,dot乘积
                    yhat = self.softmax_calculation(self.W.T.dot(X[k].reshape(-1, 1)))
                    # increment 变量用于存储每个小批量数据的梯度之和
                    # feature行1列*1行typenum列
                    increment += X[k].reshape(-1, 1).dot((self.change_y(y[k]) - yhat).T)
                # print(i * mini_size)
                # print(yhat)
                # 更新模型的权重矩阵self.W。
                self.W += alpha / mini_size * increment

        # 当训练策略为随机梯度下降时执行的代码
        elif strategy == "shuffle":
            for i in range(times):
                k = random.randint(0, self.sample - 1)
                yhat = self.softmax_calculation(self.W.T.dot(X[k].reshape(-1, 1)))
                # self.change_y(y[k]) - yhat表示预测值与真实值之间的误差
                increment = X[k].reshape(-1, 1).dot((self.change_y(y[k]) - yhat).T)
                self.W += alpha * increment

        # 当训练策略为批量梯度下降时执行的代码
        elif strategy=="batch":
            for i in range(times):
                increment = numpy.zeros((self.feature, self.typenum))
                for j in range(self.sample):
                    yhat = self.softmax_calculation(self.W.T.dot(X[j].reshape(-1, 1)))
                    increment += X[j].reshape(-1, 1).dot((self.change_y(y[j]) - yhat).T)
                self.W += alpha / self.sample * increment
        else:
            raise Exception("Unknown strategy")

(三)损失函数

损失函数,又称误差函数,用来衡量算法的运行情况,估量模型的预测值与真实值的不⼀致程度,是⼀个非负实值函数,通常使用L(Y,f(x))来表示。损失函数越小,模型的鲁棒性就越好。

损失函数可分为经验风险损失函数和结构风险损失函数。经验风险损失函数指预测结果和实际结果的差别,结构风险损失函数是在经验风险损失函数上加上正则项。

对于交叉熵损失函数,也就是负对数似然函数,其表示形式为:

(四)梯度下降

梯度下降是机器学习中常见的优化算法之一,作用如下:

(1)梯度下降是迭代法的一种,可用于求解最小二乘问题。

(2)通过梯度下降法迭代求解,得到最小化的损失函数和模型参数值。

(3)如果我们需要求解损失函数的最大值,可通过梯度上升法来迭代。梯度下降法和梯度上升法可相互转换。

核心思想:

(1)初始化参数,随机选取取值范围内的任意数;

(2)迭代操作: a)计算当前梯度; b)修改新的变量; c)计算朝最陡的下坡方向走一步; d)判断是否需要终止,如否,返回a);

(3)得到全局最优解或者接近全局最优解。 

梯度下降法主要有随机梯度下降法(shuffle)和批量梯度下降法(batch&mini-batch)。 

1、批量梯度下降法(batch)

目标函数是整个训练集上的风险函数,这种方式称为批量梯度下降法(Batch Gradient Descent,BGD)。批量梯度下降法在每次迭代时需要计算每个样本上损失函数的梯度并求和,因此当训练集中的样本数量𝑁很大时,空间复杂度较高,每次迭代的计算开销也较大。

2、随机梯度下降法(shuffle)

为了减少每次迭代的计算复杂度,我们也可以在每次迭代时只采集一个样本,计算这个样本损失函数的梯度并更新参数,即随机梯度下降法 (Stochastic Gradient Descent,SGD)。 随机梯度下降法也叫作增量梯度下降法,当经过足够次数的迭代时,随机梯度下降也可以收敛到局部最优解。

3、小批量梯度下降法(mini-batch)

小批量梯度下降法(Mini-Batch Gradient Descent)是批量梯度下降和随机梯度下降的折中。每次迭代时,我们随机选取一小部分训练样本来计算梯度并更新参数,这样既可以兼顾随机梯度下降法的优点,也可以提高训练效率。

4、三者优缺点比较

方法优缺点
batch

a)采用所有数据来梯度下降。

b)批量梯度下降法在样本量很大的时候,训练速度慢。

shuffle

a)只用一个样本进行梯度下降。

b)训练速度很快。

c)仅仅使用⼀个样本决定梯度方向,导致解可能不是全局最优。

d)随机梯度下降法一次迭代一个样本,导致迭代方向变化很大,不能很快的收敛到局部最优解。

mini-batch

a)收敛快、计算开销小

(五)训练集/验证集/测试集的划分

二、实验 

样本个数:20000
训练集:测试集 : 8:2
学习率:[0.001, 0.01, 0.1, 1, 10, 100, 1000, 10000]
Mini-Batch 大小:样本的1%(200)

梯度次数总数:100000


注意:为了比较三种梯度下降的优劣,我们需要控制每种梯度下降的计算梯度的次数一致。

假设样本量为1000,那么batch循环一次就需要计算1000次梯度,而shuffle循环一次只需要计算1次梯度,mini-batch循环一次需要计算1%*1000=10次梯度。显然,如果规定相同的循环次数,那么shuffle和mini-batch的结果会有失偏颇,因此需要规定相同的计算梯度的次数。

例如,总计算次数为10000,样本量为1000,batch就只能循环10000/1000=10次,shuffle能循环10000/1=10000次,mini-batch能循环10000/(1%*1000)=1000次。

(一)代码

import matplotlib.pyplot
from softmax_regression import Softmax

"""画出同一个分类器(softmax)在不同参数下的性能曲线"""
def alpha_gradient_plot(bag,gram, total_times, mini_size):
    # 不同学习率
    alphas = [0.001, 0.01, 0.1, 1, 10, 100, 1000, 10000]
    # Bag of words特征提取的方法下,对于每一个学习率alpha,分别进行三种训练方式的测试

    # Shuffle
    shuffle_train = list()
    shuffle_test = list()
    for alpha in alphas:
        soft = Softmax(len(bag.train), 5, bag.len)
        soft.regression(bag.train_matrix, bag.train_y, alpha, total_times, "shuffle")
        r_train, r_test = soft.correct_rate(bag.train_matrix, bag.train_y, bag.test_matrix, bag.test_y)
        shuffle_train.append(r_train)
        shuffle_test.append(r_test)

    # Batch
    batch_train = list()
    batch_test = list()
    for alpha in alphas:
        soft = Softmax(len(bag.train), 5, bag.len)
        soft.regression(bag.train_matrix, bag.train_y, alpha, int(total_times/bag.max_item), "batch")
        r_train, r_test = soft.correct_rate(bag.train_matrix, bag.train_y, bag.test_matrix, bag.test_y)
        batch_train.append(r_train)
        batch_test.append(r_test)

    # Mini-batch
    mini_train = list()
    mini_test = list()
    for alpha in alphas:
        soft = Softmax(len(bag.train), 5, bag.len)
        soft.regression(bag.train_matrix, bag.train_y, alpha, int(total_times/mini_size), "mini",mini_size)
        r_train, r_test= soft.correct_rate(bag.train_matrix, bag.train_y, bag.test_matrix, bag.test_y)
        mini_train.append(r_train)
        mini_test.append(r_test)
    matplotlib.pyplot.subplot(2,2,1)
    matplotlib.pyplot.semilogx(alphas,shuffle_train,'r--',label='shuffle')
    matplotlib.pyplot.semilogx(alphas, batch_train, 'k--', label='batch')
    matplotlib.pyplot.semilogx(alphas, mini_train, 'b--', label='mini-batch')
    matplotlib.pyplot.semilogx(alphas,shuffle_train, 'ro-', alphas, batch_train, 'b+-',alphas, mini_train, 'k^-')
    matplotlib.pyplot.legend()
    matplotlib.pyplot.title("Bag of words -- Training Set")
    matplotlib.pyplot.xlabel("Learning Rate")
    matplotlib.pyplot.ylabel("Accuracy")
    matplotlib.pyplot.ylim(0,1)

    matplotlib.pyplot.subplot(2, 2, 2)
    matplotlib.pyplot.semilogx(alphas, shuffle_test, 'r--', label='shuffle')
    matplotlib.pyplot.semilogx(alphas, batch_test, 'k--', label='batch')
    matplotlib.pyplot.semilogx(alphas, mini_test, 'b--', label='mini-batch')
    matplotlib.pyplot.semilogx(alphas, shuffle_test, 'ro-', alphas, batch_test, 'b+-', alphas, mini_test, 'k^-')
    matplotlib.pyplot.legend()
    matplotlib.pyplot.title("Bag of words -- Test Set")
    matplotlib.pyplot.xlabel("Learning Rate")
    matplotlib.pyplot.ylabel("Accuracy")
    matplotlib.pyplot.ylim(0, 1)

    # N-gram
    # Shuffle
    shuffle_train = list()
    shuffle_test = list()
    for alpha in alphas:
        soft = Softmax(len(gram.train), 5, gram.len)
        soft.regression(gram.train_matrix, gram.train_y, alpha, total_times, "shuffle")
        r_train, r_test = soft.correct_rate(gram.train_matrix, gram.train_y, gram.test_matrix, gram.test_y)
        shuffle_train.append(r_train)
        shuffle_test.append(r_test)

    # Batch
    batch_train = list()
    batch_test = list()
    for alpha in alphas:
        soft = Softmax(len(gram.train), 5, gram.len)
        soft.regression(gram.train_matrix, gram.train_y, alpha, int(total_times / gram.max_item), "batch")
        r_train, r_test = soft.correct_rate(gram.train_matrix, gram.train_y, gram.test_matrix, gram.test_y)
        batch_train.append(r_train)
        batch_test.append(r_test)

    # Mini-batch
    mini_train = list()
    mini_test = list()
    for alpha in alphas:
        soft = Softmax(len(gram.train), 5, gram.len)
        soft.regression(gram.train_matrix, gram.train_y, alpha, int(total_times / mini_size), "mini", mini_size)
        r_train, r_test = soft.correct_rate(gram.train_matrix, gram.train_y, gram.test_matrix, gram.test_y)
        mini_train.append(r_train)
        mini_test.append(r_test)

    matplotlib.pyplot.subplot(2, 2, 3)
    matplotlib.pyplot.semilogx(alphas, shuffle_train, 'r--', label='shuffle')
    matplotlib.pyplot.semilogx(alphas, batch_train, 'b--', label='batch')
    matplotlib.pyplot.semilogx(alphas, mini_train, 'k--', label='mini-batch')
    matplotlib.pyplot.semilogx(alphas, shuffle_train, 'ro-', alphas, batch_train, 'b+-', alphas, mini_train, 'k^-')
    matplotlib.pyplot.legend()
    matplotlib.pyplot.title("N-gram -- Training Set")
    matplotlib.pyplot.xlabel("Learning Rate")
    matplotlib.pyplot.ylabel("Accuracy")
    matplotlib.pyplot.ylim(0, 1)

    matplotlib.pyplot.subplot(2, 2, 4)
    matplotlib.pyplot.semilogx(alphas, shuffle_test, 'r--', label='shuffle')
    matplotlib.pyplot.semilogx(alphas, batch_test, 'b--', label='batch')
    matplotlib.pyplot.semilogx(alphas, mini_test, 'k--', label='mini-batch')
    matplotlib.pyplot.semilogx(alphas, shuffle_test, 'ro-', alphas, batch_test, 'b+-', alphas, mini_test, 'k^-')
    matplotlib.pyplot.legend()
    matplotlib.pyplot.title("N-gram -- Test Set")
    matplotlib.pyplot.xlabel("Learning Rate")
    matplotlib.pyplot.ylabel("Accuracy")
    matplotlib.pyplot.ylim(0, 1)

    matplotlib.pyplot.tight_layout()
    matplotlib.pyplot.show()

(二)结果分析

分别对训练集和测试集不同特征提取方法、学习率、梯度下降方法的分类器性能进行可视化:

整体看来,训练集和测试集的准确率分布和趋势是一致的,训练集精度整体高于测试集。

对于不同特征提取方法,N-gram的准确率要明显高于BOW词袋模型,与上述理论一致,N-gram考虑了词序特征,因此特征提取效果更好。

对于不同的学习率,小的学习率并未让模型收敛,而大的学习率相比之下会更好。

对于不同的梯度下降方法,mini-batch和batch在学习率较小时表现趋势几乎一致,然而在大的学习率下,mini-batch有着更好的表现。shuffle在学习率较小时明显比其他两种方法准确率高得多,在大的学习率下和mini-batch准确率相差不大,但依然是最优的,整体看来batch表现最差。

总结:

由于是初学者,学习过程中参考了很多大佬的资料和代码,均附上参考链接:

1、https://blog.csdn.net/Elenstone/article/details/105134863

2、https://blog.csdn.net/qq_42365109/article/details/114844020

3、https://www.cnblogs.com/bep-feijin/p/9430164.html

4、邱锡鹏——《神经网络与深度学习》

5、https://zhuanlan.zhihu.com/p/95132284

6、https://blog.csdn.net/tian_tian_hero/article/details/89409472

7、https://blog.csdn.net/GentleCP/article/details/108035696
以上就是NLP-Beginner的任务一,欢迎各位前辈批评指正!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q小Q琪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值