week07_nlp文本分类任务
1、文本分类
使用场景
自定义任务类别
类别的定义方式是任意的
只要人能够基于文本能够判断,都可以作为分类类别 如:
垃圾邮件分类
对话、文章是否与汽车交易相关
文章风格是否与某作者风格一致
文章是否是机器生成
合同文本是否符合规范
文章适合阅读人群(未成年、中年、老年、孕妇等)
机器学习(有监督学习)
2、贝叶斯算法
贝叶斯公式
全概率公式:
贝叶斯公式在NLP中的应用-文本分类
一个合理假设: 文本属于哪个类别,与文本中包含哪些词相关
任务: 知道文本中有哪些词,预测文本属于某类别的概率
假定有3个类别A1, A2, A3
一个文本S有n个词组成,W1, W2, W3....Wn
想要计算文本S属于A1类别的概率P(A1|S) = P(A1|W1, W2, W3....Wn)
#贝叶斯公式
P(A1|S) = P(W1, W2…Wn|A1) * P(A1) / P(W1,W2…Wn)
P(A2|S) = P(W1, W2…Wn|A2) * P(A2) / P(W1,W2…Wn)
P(A3|S) = P(W1, W2…Wn|A3) * P(A3) / P(W1,W2…Wn)
#词的独立性假设
P(W1, W2…Wn|A3) = P(W1|A3) * P(W2|A3)…P(Wn|A3)
import math
import jieba
import re
import os
import json
from collections import defaultdict
jieba.initialize()
"""
贝叶斯分类实践
P(A|B) = (P(A) * P(B|A)) / P(B)
事件A:文本属于类别x1。文本属于类别x的概率,记做P(x1)
事件B:文本为s (s=w1w2w3..wn)
P(x1|s) = 文本为s,属于x1类的概率. #求解目标#
P(x1|s) = P(x1|w1, w2, w3...wn) = P(w1, w2..wn|x1) * P(x1) / P(w1, w2, w3...wn)
P(x1) 任意样本属于x1的概率。x1样本数/总样本数
P(w1, w2..wn|x1) = P(w1|x1) * P(w2|x1)...P(wn|x1) 词的独立性假设
P(w1|x1) x1类样本中,w1出现的频率
公共分母的计算,使用全概率公式:
P(w1, w2, w3...wn) = P(w1,w2..Wn|x1)*P(x1) + P(w1,w2..Wn|x2)*P(x2) ... P(w1,w2..Wn|xn)*P(xn)
"""
class BayesApproach:
def __init__(self, data_path):
self.p_class = defaultdict(int)
self.word_class_prob = defaultdict(dict)
self.load(data_path)
def load(self, path):
self.class_name_to_word_freq = defaultdict(dict)
self.all_words = set() #汇总一个词表
with open(path, encoding="utf8") as f:
for line in f:
line = json.loads(line)
class_name = line["tag"]
title = line["title"]
words = jieba.lcut(title)
self.all_words = self.all_words.union(set(words))
self.p_class[class_name] += 1 #记录每个类别样本数量
word_freq = self.class_name_to_word_freq[class_name]
#记录每个类别下的词频
for word in words:
if word not in word_freq:
word_freq[word] = 1
else:
word_freq[word] += 1
self.freq_to_prob()
return
#将记录的词频和样本频率都转化为概率
def freq_to_prob(self):
#样本概率计算
total_sample_count = sum(self.p_class.values())
self.p_class = dict((c, self.p_class[c] / total_sample_count) for c in self.p_class)
#词概率计算
self.word_class_prob = defaultdict(dict)
for class_name, word_freq in self.class_name_to_word_freq.items():
total_word_count = sum(count for count in word_freq.values()) #每个类别总词数
for word in word_freq:
#加1平滑,避免出现概率为0,计算P(wn|x1)
prob = (word_freq[word] + 1) / (total_word_count + len(self.all_words))
self.word_class_prob[class_name][word] = prob
self.word_class_prob[class_name]["<unk>"] = 1/(total_word_count + len(self.all_words))
return
#P(w1|x1) * P(w2|x1)...P(wn|x1)
def get_words_class_prob(self, words, class_name):
result = 1
for word in words:
unk_prob = self.word_class_prob[class_name]["<unk>"]
result *= self.word_class_prob[class_name].get(word, unk_prob)
return result
#计算P(w1, w2..wn|x1) * P(x1)
def get_class_prob(self, words, class_name):
#P(x1)
p_x = self.p_class[class_name]
# P(w1, w2..wn|x1) = P(w1|x1) * P(w2|x1)...P(wn|x1)
p_w_x = self.get_words_class_prob(words, class_name)
return p_x * p_w_x
#做文本分类
def classify(self, sentence):
words = jieba.lcut(sentence) #切词
results = []
for class_name in self.p_class:
prob = self.get_class_prob(words, class_name) #计算class_name类概率
results.append([class_name, prob])
results = sorted(results, key=lambda x:x[1], reverse=True) #排序
#计算公共分母:P(w1, w2, w3...wn) = P(w1,w2..Wn|x1)*P(x1) + P(w1,w2..Wn|x2)*P(x2) ... P(w1,w2..Wn|xn)*P(xn)
#不做这一步也可以,对顺序没影响,只不过得到的不是0-1之间的概率值
pw = sum([x[1] for x in results]) #P(w1, w2, w3...wn)
results = [[c, prob/pw] for c, prob in results]
#打印结果
for class_name, prob in results:
print("属于类别[%s]的概率为%f" % (class_name, prob))
return results
if __name__ == "__main__":
path = "../data/train_tag_news.json"
ba = BayesApproach(path)
query = "中国三款导弹可发射多弹头 美无法防御很急躁"
ba.classify(query)
贝叶斯算法优缺点分析:
优点:
(一)简单高效
- 贝叶斯算法基于贝叶斯定理,其计算过程相对简单直接。在 NLP 任务中,例如文本分类,它可以通过计算文档属于各个类别(如体育类、科技类等)的概率来进行分类。
(二)对小规模数据有效
- 在 NLP 领域,有时候获取大规模的标注数据是很困难的。贝叶斯算法在数据量较小的情况下也能取得不错的效果。
- 比如在情感分析任务中,对于一个新的产品评论数据集,当只有少量的标注评论(如几百条)时,贝叶斯分类器可以利用先验知识(如从其他类似产品评论中获取的情感倾向概率分布)结合新数据进行分类。它可以通过少量的正负面评价词汇出现的频率来估计评论的情感倾向。
(三)结合先验知识
- 贝叶斯算法能够很好地结合先验知识。在 NLP 应用中,我们可能已经对某些语言现象或者文本类别有一定的了解。
- 例如在文本主题分类中,如果我们已经知道某类主题(如医学主题)文本中一些高频词汇(如 “疾病”“治疗”“症状” 等)的概率分布,那么在新的文本分类任务中,这些先验概率可以作为很好的初始值或者辅助信息融入贝叶斯分类器中,帮助提高分类的准确性。
(四)概率解释性强
- 贝叶斯算法输出的是文本属于各个类别的概率。这在 NLP 任务中有很好的解释性。
- 比如在机器翻译的候选译文选择中,贝叶斯模型可以计算出每个译文是正确翻译的概率。用户或者开发者可以根据这个概率来评估译文的可靠性。并且在一些需要进行不确定性处理的 NLP 应用场景中,如信息检索中对检索结果相关性的评估,概率输出能够很好地体现不确定性程度。
缺点:
(一)独立性假设问题
- 贝叶斯算法(特别是朴素贝叶斯)通常假设文本中的词汇是相互独立的。但在实际的 NLP 任务中,这种假设往往不成立。
- 在自然语言中,词汇之间存在很强的语义和语法关联。例如在句子 “我喜欢吃苹果” 中,“吃” 和 “苹果” 这两个词之间存在动宾关系,并不是独立的。如果使用朴素贝叶斯算法进行文本分类,忽略这种关联性可能会导致错误的分类结果。比如在美食类和运动类文本分类中,仅仅根据词汇出现的独立概率,可能会将包含 “苹果” 和 “吃” 的句子错误地分类到运动类,因为 “苹果” 也可能在运动类文本中出现(如苹果手机与运动软件相关的内容),但如果考虑词汇之间的关系就可以正确分类到美食类。
(二)数据稀疏问题
- 在 NLP 中,词汇表通常非常庞大。当遇到一些罕见词汇或者在训练数据中未出现的词汇时,贝叶斯算法会面临数据稀疏问题。
- 例如在处理专业领域(如古生物学)的文本时,会出现很多生僻的词汇。这些词汇在训练数据中的出现次数可能极少甚至为零。对于未出现的词汇,计算其条件概率时会出现困难。因为按照贝叶斯算法的计算方式,如果词汇在类别的训练数据中未出现,那么,这会导致整个文档的概率计算结果出现偏差,影响分类等任务的准确性。
(三)对特征工程依赖较强
- 贝叶斯算法在 NLP 中的性能很大程度上依赖于特征的选择和表示。
- 在文本处理中,需要将文本转换为合适的特征向量形式,如词袋模型(Bag - of - Words)。如果特征选择不合理,例如选择了一些没有区分度的词汇作为特征,或者没有考虑词汇的词形变化(如动词的不同时态)等因素,贝叶斯分类器的性能将会受到很大影响。而且对于不同的 NLP 任务(如文本分类、命名实体识别等),需要设计不同的特征工程方法来适应任务需求,这增加了使用贝叶斯算法的复杂性。
(四)难以处理复杂语义关系
- 自然语言具有丰富的语义关系,如隐喻、反讽等。贝叶斯算法很难捕捉这些复杂的语义关系。
- 例如在一个包含反讽语句的情感分析任务中,如 “这部电影真‘好’,看得我都睡着了”,从词汇表面看,出现了 “好” 这个正面词汇,但实际上表达的是负面情感。贝叶斯算法仅基于词汇出现的概率很难理解这种反讽的语义,从而可能会错误地将其分类为正面评价。
3、支持向量机
(SVM:support vector machine)
- 定义:支持向量机是一种监督式学习的分类算法,其基本思想是在特征空间中寻找一个最优的超平面,将不同类别的样本尽可能地分开。这个超平面是通过最大化两类样本到超平面的最小距离(也称为 margin)来确定的。
线性可分情况
线性不可分情况
#!/usr/bin/env python3
#coding: utf-8
#使用基于词向量的分类器
#对比几种模型的效果
import json
import jieba
import numpy as np
from gensim.models import Word2Vec
from sklearn.metrics import classification_report
from sklearn.svm import SVC
from collections import defaultdict
LABELS = {'健康': 0, '军事': 1, '房产': 2, '社会': 3, '国际': 4,
'旅游': 5, '彩票': 6, '时尚': 7, '文化': 8, '汽车': 9,
'体育': 10, '家居': 11, '教育': 12, '娱乐': 13,
'科技': 14, '股票': 15, '游戏': 16, '财经': 17}
#输入模型文件路径
#加载训练好的模型
def load_word2vec_model(path):
model = Word2Vec.load(path)
return model
#加载数据集
def load_sentence(path, model):
sentences = []
labels = []
with open(path, encoding="utf8") as f:
for line in f:
line = json.loads(line)
title, content = line["title"], line["content"]
sentences.append(" ".join(jieba.lcut(title)))
labels.append(line["tag"])
train_x = sentences_to_vectors(sentences, model)
train_y = label_to_label_index(labels)
return train_x, train_y
#tag标签转化为类别标号
def label_to_label_index(labels):
return [LABELS[y] for y in labels]
#文本向量化,使用了基于这些文本训练的词向量
def sentences_to_vectors(sentences, model):
vectors = []
for sentence in sentences:
words = sentence.split()
vector = np.zeros(model.vector_size)
for word in words:
try:
vector += model.wv[word]
# vector = np.max([vector, model.wv[word]], axis=0)
except KeyError:
vector += np.zeros(model.vector_size)
vectors.append(vector / len(words))
return np.array(vectors)
def main():
model = load_word2vec_model("model.w2v")
train_x, train_y = load_sentence("../data/train_tag_news.json", model)
test_x, test_y = load_sentence("../data/valid_tag_news.json", model)
classifier = SVC()
classifier.fit(train_x, train_y)
y_pred = classifier.predict(test_x)
print(classification_report(test_y, y_pred))
if __name__ == "__main__":
main()
核函数
在支持向量机(SVM)中,核函数是一种将低维空间中的数据映射到高维空间的函数。其主要目的是解决在原始低维空间中线性不可分的数据分类问题。例如,在二维平面上,一些数据点可能无法通过一条直线(线性分类器)进行分类,但通过核函数将数据映射到三维空间后,就有可能用一个平面(在高维空间中仍然是线性分类器)将数据分开。
- 核函数的性质
- 正定性质:核函数必须是正定核,即对于任意有限个点,矩阵是半正定矩阵。这个性质保证了基于核函数的二次规划问题有解,并且使得 SVM 能够有效地进行分类。
- 可重复性:对于相同的输入和,核函数应该总是返回相同的结果。这是算法稳定性的要求,确保在不同的训练阶段或者不同的计算环境下,SVM 的分类决策不会因为核函数的不确定性而发生变化。
线性核函数 K(x, z) = x · z
它对应的是原始的线性 SVM,适用于数据在原始空间中线性可分的情况。例如,对于一些简单的数据集,如二维平面上可以通过一条直线分开的两类点,使用线性核函数的 SVM 就可以很好地进行分类。
多项式核函数
其中d是多项式的次数。当d=1时,多项式核函数就退化为线性核函数。随着d的增大,分类边界会变得更加复杂。例如,对于一些具有弯曲边界的数据集,适当选择d值的多项式核函数可以更好地拟合数据,将不同类别的数据分开。
高斯核函数(RBF 核函数)
其中是带宽参数。高斯核函数可以将数据映射到无限维空间,它具有很强的灵活性,能够处理各种复杂的数据集。例如,在处理具有多个聚类且形状不规则的数据集时,高斯核函数通常能够取得较好的分类效果。的值对分类结果有很大的影响,较小的会使模型对训练数据拟合得很好,但可能会出现过拟合;较大的会使模型更平滑,但可能会丢失一些细节,导致欠拟合。
Sigmoid 核函数
其中和是参数。它在神经网络中也有类似的形式,在某些情况下可以用于处理非线性分类问题。
解决多分类
假设要解决一个K分类问题,即有K个目标类别
one vs one方式
建立 K(K - 1)/2 个svm分类器,每个分类器负责K个类别中的两个类别,判断输入样本属于哪个类别 对于一个待预测的样本,使用所有分类器进行分类,最后保留被预测词数最多的类别
假设类别有[A,B,C]
X->SVM(A,B)->A X->SVM(A,C)->A X->SVM(B,C)->B
最终判断 X->A
one vs rest方式
建立K个svm分类器,每个分类器负责划分输入样本属于K个类别中的某一个类别,还是其他类别 最后保留预测分值最高的类别
假设类别有[A,B,C]
X->SVM(A,rest)->0.1 X->SVM(B,rest)->0.2 X->SVM(C,rest)->0.5 最终判断 X->C
支持向量机优缺点分析
- 优点
- 高准确性
- 少数支持向量决定了最终结果,对异常值不敏感。支持向量机在处理高维数据时表现出色。例如在文本分类任务中,文本通常被表示为高维的词向量空间,SVM 能够有效利用这些高维特征找到一个最优的分类超平面。通过合理选择核函数,它可以挖掘数据中的复杂模式,从而实现高精度的分类。在许多实际的数据集测试中,SVM 的分类准确率能够达到较高的水平,与其他机器学习算法相比有很强的竞争力。
- 对小样本数据有效
- 当训练数据样本数量相对较少时,SVM 仍然能够很好地工作。例如在一些医学研究中,某些罕见疾病的数据样本可能非常有限,SVM 可以根据这些少量的样本学习到疾病特征与正常状态之间的分类边界。因为它主要关注的是支持向量,即那些对分类边界起关键作用的样本点,而不是全部的数据点,所以在小样本情况下也能够构建出有效的分类模型。
- 泛化能力较强
- SVM 通过最大化分类间隔来构建模型,这种方式使得模型对新数据有较好的泛化能力。例如在图像识别中,经过训练的 SVM 模型在面对新的未见过的图像时,能够根据已学习到的特征和分类边界,较为准确地判断图像所属的类别。并且,适当的正则化参数(如惩罚参数 C)可以控制模型的复杂度,避免过拟合,进一步增强模型的泛化性能。
- 能够处理非线性数据(通过核函数)
- 对于非线性可分的数据,SVM 的核函数技巧是一个非常强大的工具。例如在一些复杂的生物数据或工业生产过程数据中,数据的分布往往是非线性的。通过使用高斯核、多项式核等核函数,SVM 可以将原始数据映射到高维特征空间,在这个空间中找到线性可分的超平面来进行分类。这使得 SVM 能够处理各种复杂的数据分布情况。
- 高准确性
- 缺点
- 计算复杂度高(对于大规模数据)
- 当处理大规模数据集时,SVM 的训练时间会显著增加。因为它需要求解二次规划问题来确定最优的分类超平面。例如在处理互联网大数据,如海量的网页文本数据或用户行为数据时,SVM 的计算成本会变得很高。特别是当数据样本数量和特征维度都很大时,求解二次规划问题所需的内存和计算时间可能会达到无法接受的程度。
- 对参数敏感(如核函数和惩罚参数)
- SVM 的性能很大程度上依赖于核函数的选择和参数(如惩罚参数 C 和核函数的参数,如高斯核中的)的设置。如果核函数选择不当或者参数设置不合理,可能会导致模型性能下降。例如在使用高斯核函数时,如果设置得过大,模型可能会过于平滑,导致欠拟合;如果设置得过小,模型可能会过于复杂,导致过拟合。而且,确定这些最优参数通常需要进行复杂的交叉验证等调优过程。
- 解释性相对较差
- 与决策树等一些具有直观解释性的模型相比,SVM 是一个相对复杂的黑盒模型。虽然可以通过一些方法(如分析支持向量)来部分解释模型的决策过程,但总体来说,它很难像决策树那样清晰地展示出每个特征对分类结果的具体影响。例如在医疗诊断应用中,医生可能更希望能够直观地了解模型是基于哪些因素做出诊断的,而 SVM 在这方面的解释性相对不足。
- 计算复杂度高(对于大规模数据)
4、文本分类应用
深度学习-pipeline
fastText
一句话总结:
- fastText 是 Facebook 开发的一个用于文本处理的库,它在文本分类和词向量学习方面表现出色。它的模型结构简单高效,能够快速处理大规模的文本数据。其核心思想是将文本看作是由单词和字符组成的序列,通过对这些序列进行操作来实现文本分类等任务。
x -> Embedding -> Mean Pooling -> Label
TextRNN
一句话总结:
- TextRNN(循环神经网络用于文本处理)是一种专门用于处理文本序列的深度学习模型。它基于循环神经网络(RNN)架构,能够有效地处理文本的顺序信息,这对于理解自然语言的语义和语法等方面非常关键。与传统的机器学习方法相比,TextRNN 可以自动学习文本的特征表示,而不需要人工进行复杂的特征工程。
x –>embedding –>BiLSTM –>Dropout ->LSTM ->Linear –>softmax –>y
RNN
隐向量按时间步向后传递,起到记忆的作用
LSTM
一句话总结:
主要用于处理和预测时间序列中间隔和延迟非常长的重要事件。它能够有效地解决传统 RNN 中的梯度消失和梯度爆炸问题,从而更好地对长序列数据进行建模。
import torch
import torch.nn as nn
import numpy as np
'''
用矩阵运算的方式复现一些基础的模型结构
清楚模型的计算细节,有助于加深对于模型的理解,以及模型转换等工作
'''
#构造一个输入
length = 6
input_dim = 12
hidden_size = 7
x = np.random.random((length, input_dim))
# print(x)
#使用pytorch的lstm层
torch_lstm = nn.LSTM(input_dim, hidden_size, batch_first=True)
for key, weight in torch_lstm.state_dict().items():
print(key, weight.shape)
def sigmoid(x):
return 1/(1 + np.exp(-x))
#将pytorch的lstm网络权重拿出来,用numpy通过矩阵运算实现lstm的计算
def numpy_lstm(x, state_dict):
weight_ih = state_dict["weight_ih_l0"].numpy()
weight_hh = state_dict["weight_hh_l0"].numpy()
bias_ih = state_dict["bias_ih_l0"].numpy()
bias_hh = state_dict["bias_hh_l0"].numpy()
#pytorch将四个门的权重拼接存储,我们将它拆开
w_i_x, w_f_x, w_c_x, w_o_x = weight_ih[0:hidden_size, :], \
weight_ih[hidden_size:hidden_size*2, :],\
weight_ih[hidden_size*2:hidden_size*3, :],\
weight_ih[hidden_size*3:hidden_size*4, :]
w_i_h, w_f_h, w_c_h, w_o_h = weight_hh[0:hidden_size, :], \
weight_hh[hidden_size:hidden_size * 2, :], \
weight_hh[hidden_size * 2:hidden_size * 3, :], \
weight_hh[hidden_size * 3:hidden_size * 4, :]
b_i_x, b_f_x, b_c_x, b_o_x = bias_ih[0:hidden_size], \
bias_ih[hidden_size:hidden_size * 2], \
bias_ih[hidden_size * 2:hidden_size * 3], \
bias_ih[hidden_size * 3:hidden_size * 4]
b_i_h, b_f_h, b_c_h, b_o_h = bias_hh[0:hidden_size], \
bias_hh[hidden_size:hidden_size * 2], \
bias_hh[hidden_size * 2:hidden_size * 3], \
bias_hh[hidden_size * 3:hidden_size * 4]
w_i = np.concatenate([w_i_h, w_i_x], axis=1)
w_f = np.concatenate([w_f_h, w_f_x], axis=1)
w_c = np.concatenate([w_c_h, w_c_x], axis=1)
w_o = np.concatenate([w_o_h, w_o_x], axis=1)
b_f = b_f_h + b_f_x
b_i = b_i_h + b_i_x
b_c = b_c_h + b_c_x
b_o = b_o_h + b_o_x
c_t = np.zeros((1, hidden_size))
h_t = np.zeros((1, hidden_size))
sequence_output = []
for x_t in x:
x_t = x_t[np.newaxis, :]
hx = np.concatenate([h_t, x_t], axis=1)
# f_t = sigmoid(np.dot(x_t, w_f_x.T) + b_f_x + np.dot(h_t, w_f_h.T) + b_f_h)
f_t = sigmoid(np.dot(hx, w_f.T) + b_f)
# i_t = sigmoid(np.dot(x_t, w_i_x.T) + b_i_x + np.dot(h_t, w_i_h.T) + b_i_h)
i_t = sigmoid(np.dot(hx, w_i.T) + b_i)
# g = np.tanh(np.dot(x_t, w_c_x.T) + b_c_x + np.dot(h_t, w_c_h.T) + b_c_h)
g = np.tanh(np.dot(hx, w_c.T) + b_c)
c_t = f_t * c_t + i_t * g
# o_t = sigmoid(np.dot(x_t, w_o_x.T) + b_o_x + np.dot(h_t, w_o_h.T) + b_o_h)
o_t = sigmoid(np.dot(hx, w_o.T) + b_o)
h_t = o_t * np.tanh(c_t)
sequence_output.append(h_t)
return np.array(sequence_output), (h_t, c_t)
torch_sequence_output, (torch_h, torch_c) = torch_lstm(torch.Tensor([x]))
numpy_sequence_output, (numpy_h, numpy_c) = numpy_lstm(x, torch_lstm.state_dict())
print(torch_sequence_output)
print(numpy_sequence_output)
print("--------")
print(torch_h)
print(numpy_h)
print("--------")
print(torch_c)
print(numpy_c)
#############################################################
#使用pytorch的GRU层
torch_gru = nn.GRU(input_dim, hidden_size, batch_first=True)
# for key, weight in torch_gru.state_dict().items():
# print(key, weight.shape)
#将pytorch的GRU网络权重拿出来,用numpy通过矩阵运算实现GRU的计算
def numpy_gru(x, state_dict):
weight_ih = state_dict["weight_ih_l0"].numpy()
weight_hh = state_dict["weight_hh_l0"].numpy()
bias_ih = state_dict["bias_ih_l0"].numpy()
bias_hh = state_dict["bias_hh_l0"].numpy()
#pytorch将3个门的权重拼接存储,我们将它拆开
w_r_x, w_z_x, w_x = weight_ih[0:hidden_size, :], \
weight_ih[hidden_size:hidden_size * 2, :],\
weight_ih[hidden_size * 2:hidden_size * 3, :]
w_r_h, w_z_h, w_h = weight_hh[0:hidden_size, :], \
weight_hh[hidden_size:hidden_size * 2, :], \
weight_hh[hidden_size * 2:hidden_size * 3, :]
b_r_x, b_z_x, b_x = bias_ih[0:hidden_size], \
bias_ih[hidden_size:hidden_size * 2], \
bias_ih[hidden_size * 2:hidden_size * 3]
b_r_h, b_z_h, b_h = bias_hh[0:hidden_size], \
bias_hh[hidden_size:hidden_size * 2], \
bias_hh[hidden_size * 2:hidden_size * 3]
w_z = np.concatenate([w_z_h, w_z_x], axis=1)
w_r = np.concatenate([w_r_h, w_r_x], axis=1)
b_z = b_z_h + b_z_x
b_r = b_r_h + b_r_x
h_t = np.zeros((1, hidden_size))
sequence_output = []
for x_t in x:
x_t = x_t[np.newaxis, :]
hx = np.concatenate([h_t, x_t], axis=1)
z_t = sigmoid(np.dot(hx, w_z.T) + b_z)
r_t = sigmoid(np.dot(hx, w_r.T) + b_r)
h = np.tanh(r_t * (np.dot(h_t, w_h.T) + b_h) + np.dot(x_t, w_x.T) + b_x)
h_t = (1 - z_t) * h + z_t * h_t
sequence_output.append(h_t)
return np.array(sequence_output), h_t
# torch_sequence_output, torch_h = torch_gru(torch.Tensor([x]))
# numpy_sequence_output, numpy_h = numpy_gru(x, torch_gru.state_dict())
#
# print(torch_sequence_output)
# print(numpy_sequence_output)
# print("--------")
# print(torch_h)
# print(numpy_h)
TextCNN
一句话总结:
利用一维卷积对文本进行编码,编码后的文本矩阵通过pooling转化为向量,用于分类
Gated CNN
一句话总结:
是一种结合了卷积神经网络(CNN)和门控机制的深度学习架构。它主要用于处理序列数据,如文本和语音等,通过利用卷积操作的并行性和门控机制的选择性,能够有效地提取序列中的特征,同时解决传统 CNN 在处理序列数据时可能出现的信息丢失问题。与传统的循环神经网络(RNN)和长短期记忆网络(LSTM)相比,Gated CNN 在处理长序列数据时具有更高的计算效率,并且能够在一定程度上捕捉序列中的长距离依赖关系。
5、实际项目可能存在的问题
数据稀疏问题
训练数据量小,模型在训练样本上能收敛,但预测准确率很低
解决方案:
1.标注更多的数据
2.尝试构造训练样本(数据增强)
3.更换模型(如使用预训练模型等)减少数据需求
4.增加规则弥补
5.调整阈值,用召回率换准确率
6.重新定义类别(减少类别)
标签不均衡问题
部分类别样本充裕,部分类别样本极少
类别 | 金融 | 体育 | 科技 | 教育 | 教育 |
样本数量 | 3000 | 3200 | 2800 | 50 | 50 |
解决办法:
解决数据稀疏的所有的方法依然适用
1.过采样 复制指定类别的样本,在采样中重复 √
2.降采样 减少多样本类别的采样,随机使用部分 √
3.调整样本权重 通过损失函数权重(weight)调整来体现 ×
多标签分类问题
多标签问题的转化
1.分解为多个独立的二分类问题
2.将多标签分类问题转换为多分类问题
3.更换loss直接由模型进行多标签分类 (BCEloss---专门针对多标签问题)
原始数据
Class 1 | Class 2 | Class 3 | |
X1 | 1 | 0 | 1 |
X2 | 0 | 1 | 0 |
X3 | 1 | 0 | 1 |
X4 | 0 | 1 | 1 |
X5 | 1 | 1 | 0 |
多次二分类
多标签—>多分类
直接更换loss函数
原文地址:https://blog.csdn.net/Captain823Jack/article/details/145102034
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!