NLP实战-中文新闻文本分类

目录

1、思路

2、基于paddle的ERINE模型进行迁移学习训练

3、分步实现

3.1 获取数据

(1)数据解压

(2)将文本转成变量,这里为了好计算,我只选了新闻标题做文本分类

3.2 中文分词

基于jieba的分词

基于paddlehub的lac分词

3.3 创建语料字典,并将语料序列化

基于语料本身的序列化

基于word2vec或已有字典的序列化

3.4 词嵌入

基于embedding的词嵌入

基于word2vec的词嵌入

3.5 构建分类模型

基于LSTM的分类模型:


实现环境:AI studio

1、思路

文本分类任务步骤通常是

  • 文本预处理

文本预处理的方法很多,类似于词性分析,句法分析,命名实体识别等,在进行文本分类之前,需要将文本进行结构化,常见的方法有one-hot,n_gram,word2vec等,与英文不同(可以简单用空格和符号进行分词),中文是比较紧密连接的,结构化之前需要对文本进行分词,如jieba分词,此外还需要将分词之后的语料转化为ID序列,然后进行词嵌入(word Embedding)。

  • DL特征提取和分类模型

适合文本的dl model有RNN,LSTM,GRU等。

  • 预测

训练完,然后对数据进行预测。

2、基于paddle的ERINE模型进行迁移学习训练

教程:https://aistudio.baidu.com/aistudio/projectdetail/1735533?forkThirdPart=1

3、分步实现

3.1 获取数据

(1)数据解压

!unzip  /home/aistudio/data/data8164/THUCNews.zip  

解压之后结果有14个类别,文件夹里边是新闻文本:

(2)将文本转成变量,这里为了好计算,我只选了新闻标题做文本分类

(后续觉得数据量太小,就选了全量数据来做,遇到了正则表达式处理慢和分词慢的情况,推荐FlashText,中英文符号处理)

def read_data(file_dir):    z_list = []for parent, dirnames, filenames in os.walk(file_dir):if parent==file_dir:print('父目录')else:for curDir, dirs, files in os.walk(parent):print("当前文件夹:",curDir)label = curDir.split('/')[-1]title_list = []for i in range(len(files)):fo = open(parent+'/'+files[i],'r',encoding='utf-8')title = fo.readline().strip()title_list.append((title,label))z_list.extend(title_list)return z_list
file_dir = '/home/aistudio/THUCNews'
data = read_data(file_dir)

3.2 中文分词

  • 基于jieba的分词

jieba_c_list = []
#停用词字典
stopwords = {}.fromkeys([ line.rstrip() for line in open('/home/aistudio/stopwords.txt') ])
for i in range(len(data)):s_t = data[i][0].replace(' ','')s_t = re.sub(r'\d','',s_t)s_t = re.sub(r'\((.*?)\)', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\.', '', s_t)s_t = re.sub(r'\%', '', s_t)s_t = re.sub(r'\:', '', s_t)s_t = re.sub(r'\《', '', s_t)s_t = re.sub(r'\》', '', s_t)s_list = jieba.cut(s_t,cut_all=False)final = []for seg in s_list:#    seg = seg.encode('gbk')#    print(seg)if seg not in stopwords:# print(seg)final.append(seg)jieba_c_list.append((final,data[i][1]))
  • 基于paddlehub的lac分词

lac = paddlehub.Module(name='lac')
lac_list = []
for i in range(len(data)):s_t = data[i][0].replace(' ','')s_t = re.sub(r'\d','',s_t)s_t = re.sub(r'\((.*?)\)', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\.', '', s_t)s_t = re.sub(r'\%', '', s_t)s_t = re.sub(r'\:', '', s_t)s_t = re.sub(r'\《', '', s_t)s_t = re.sub(r'\》', '', s_t)results = lac.lexical_analysis(texts=[s_t],batch_size=1)lac_ = results[0]['word']# print(lac_)lac_list.append((lac_,data[i][1]))

3.3 创建语料字典,并将语料序列化

  • 基于语料本身的序列化

(1)构建语料字典

# 构造词典,统计每个词的频率,并根据频率将每个词转换为一个整数id
def build_dict(corpus):word_freq_dict = dict()for sentence,_ in corpus:for word in sentence:if word not in word_freq_dict:word_freq_dict[word] = 0word_freq_dict[word] += 1word_freq_dict = sorted(word_freq_dict.items(), key = lambda x:x[1], reverse = True)word2id_dict = dict()word2id_freq = dict()# 一般来说,我们把oov和pad放在词典前面,给他们一个比较小的id,这样比较方便记忆,并且易于后续扩展词表# word2id_dict['[oov]'] = 0# word2id_freq[0] = 1e10# word2id_dict['[pad]'] = 1# word2id_freq[1] = 1e10for word, freq in word_freq_dict:word2id_dict[word] = len(word2id_dict)word2id_freq[word2id_dict[word]] = freqreturn word2id_freq, word2id_dictword2id_freq, word2id_dict = build_dict(jieba_c_list)
vocab_size = len(word2id_freq)
print("there are totoally %d different words in the corpus" % vocab_size)
for _, (word, word_id) in zip(range(10), word2id_dict.items()):print("word %s, its id %d, its word freq %d" % (word, word_id, word2id_freq[word_id]))

(2)ID序列化

label_dict ={'时政':1,'星座':2,'股票':3,'彩票':4,'科技':5,'娱乐':6,'房产':7,'社会':8,'财经':9,'游戏':10,'体育':11,'时尚':12,'家居':13,'教育':14}
def convert_corpus_to_id(corpus, word2id_dict):data_set = []for sentence, sentence_label in corpus:# 将句子中的词逐个替换成id,如果句子中的词不在词表内,则替换成oov# 这里需要注意,一般来说我们可能需要查看一下test-set中,句子oov的比例,# 如果存在过多oov的情况,那就说明我们的训练数据不足或者切分存在巨大偏差,需要调整sentence = [word2id_dict[word] if word in word2id_dict \else word2id_dict['[oov]'] for word in sentence]    data_set.append((sentence, label_dict[sentence_label]))return data_settrain_corpus = convert_corpus_to_id(jieba_c_list, word2id_dict)
print("%d tokens in the corpus" % len(train_corpus))
print(train_corpus[:5])
  • 基于word2vec或已有字典的序列化

from paddlehub.reader.tokenization import load_vocablabel_dict ={'时政':1,'星座':2,'股票':3,'彩票':4,'科技':5,'娱乐':6,'房产':7,'社会':8,'财经':9,'游戏':10,'体育':11,'时尚':12,'家居':13,'教育':14}# 这是把 中文词语 转化为 词表 中对应 ID 的函数
def convert_tokens_to_ids(vocab, tokens): # 输入为词表,和要转化的 textwids = [] # 初始化一个空的集合,用于存放输出#tokens = text.split(" ") # 将传入的 text 用 空格 做分割,变成 词语字符串 的列表for token in tokens: # 每次从列表里取出一个 词语wid = vocab.get(token, None)if not wid:wid = vocab["unknown"]wids.append(wid)return widsmodule = paddlehub.Module(name="word2vec_skipgram") # 实例化 word2vec_skipgram 模型vocab = load_vocab(module.get_vocab_path()) # 获得 词表 字典# 我们要获取词表,直接利用 paddlehub.reader.tokenization 中的 load_vocab 函数即可
# load_vocab 函数的输入是具体的词表文件,这里我们用 word2vec_skipgram 附带的词表
# 模块的实例化对象 module 下,用 get_vocab_path() 方法
# 该方法可以在指定的 Module 中(这里就是word2vec_skipgram)查找 assets 文件夹下面有没有 vocab.txt 文件
# 如果找到,则返回该文件的 具体文件路径
# load_vocab 函数的返回值是一个 字典,里面 key 为 词语,value 是词语对应的 IDtokens_ids = []
for item,_ in lac_list:item_ids = convert_tokens_to_ids(vocab, item) # 获得组成句子的 词语 的 ID 列表tokens_ids.append((item_ids,label_dict[_]))for i in range(5):print("token: %s; id: %s" % (lac_list[i], tokens_ids[i]))

3.4 词嵌入

  • 基于embedding的词嵌入

embedding在构建神经网络模型时使用。函数为paddle.nn.Embedding

Embedding(num_embeddings=vocab_size, embedding_dim=hidden_size, sparse=False, weight_attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Uniform(low=-init_scale, high=init_scale)))
  • 基于word2vec的词嵌入

Word2Vec有GBOW和skip-gram两种实现方式,实现的库有gensim、paddlehub,这里用gensim实现。

import gensim #将取字符串,忽略类别(已转成ID序列)
from gensim.models.word2vec import Word2Vecnum_features = 100     # Word vector dimensionality
num_workers = 8       # Number of threads to run in paralleltrain_texts = list(map(lambda x: list(x.split()), train_texts))
model = Word2Vec(train_texts, workers=num_workers, size=num_features)
model.init_sims(replace=True)# save model
model.save("./word2vec.bin")
#也可转换为txt

结果的第一行是(词数量,词向量的维度就是num_features)

第二行就是词向量ID和对应的词向量

3.5 构建分类模型

基于LSTM的分类模型(此处的class_num改为和新闻类别一致的数量):

from paddle.nn import LSTM, Embedding, Dropout, Linear
import paddle.nn.functional as F# 定义一个用于情感分类的网络实例,SentimentClassifier
class SentimentClassifier(paddle.nn.Layer):def __init__(self, hidden_size, vocab_size, class_num=14, num_steps=128, num_layers=1, init_scale=0.1, dropout=None):# 参数含义如下:# 1.hidden_size,表示embedding-size,hidden和cell向量的维度# 2.vocab_size,模型可以考虑的词表大小# 3.class_num,情感类型个数,可以是2分类,也可以是多分类# 4.num_steps,表示这个情感分析模型最大可以考虑的句子长度# 5.num_layers,表示网络的层数# 6.init_scale,表示网络内部的参数的初始化范围# 长短时记忆网络内部用了很多Tanh,Sigmoid等激活函数,这些函数对数值精度非常敏感,# 因此我们一般只使用比较小的初始化范围,以保证效果super(SentimentClassifier, self).__init__()self.hidden_size = hidden_sizeself.vocab_size = vocab_sizeself.class_num = class_numself.init_scale = init_scaleself.num_layers = num_layersself.num_steps = num_stepsself.dropout = dropout# 声明一个LSTM模型,用来把每个句子抽象成向量self.simple_lstm_rnn = LSTM(input_size=hidden_size, hidden_size=hidden_size, num_layers=num_layers)# 声明一个embedding层,用来把句子中的每个词转换为向量self.embedding = Embedding(num_embeddings=vocab_size, embedding_dim=hidden_size, sparse=False, weight_attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Uniform(low=-init_scale, high=init_scale)))# 在得到一个句子的向量表示后,需要根据这个向量表示对这个句子进行分类# 一般来说,可以把这个句子的向量表示乘以一个大小为[self.hidden_size, self.class_num]的W参数,# 并加上一个大小为[self.class_num]的b参数,从而达到把句子向量映射到分类结果的目的# 我们需要声明最终在使用句子向量映射到具体情感类别过程中所需要使用的参数# 这个参数的大小一般是[self.hidden_size, self.class_num]self.cls_fc = Linear(in_features=self.hidden_size, out_features=self.class_num, weight_attr=None, bias_attr=None)self.dropout_layer = Dropout(p=self.dropout, mode='upscale_in_train')def forward(self, input, label):# 首先我们需要定义LSTM的初始hidden和cell,这里我们使用0来初始化这个序列的记忆init_hidden_data = np.zeros((self.num_layers, batch_size, embedding_size), dtype='float32')init_cell_data = np.zeros((self.num_layers, batch_size, embedding_size), dtype='float32')# 将这些初始记忆转换为飞桨可计算的向量# 设置stop_gradient=True,避免这些向量被更新,从而影响训练效果init_hidden = paddle.to_tensor(init_hidden_data)init_hidden.stop_gradient = Trueinit_cell = paddle.to_tensor(init_cell_data)init_cell.stop_gradient = Trueinit_h = paddle.reshape(init_hidden, shape=[self.num_layers, -1, self.hidden_size])init_c = paddle.reshape(init_cell, shape=[self.num_layers, -1, self.hidden_size])# 将输入的句子的mini-batch转换为词向量表示x_emb = self.embedding(input)x_emb = paddle.reshape(x_emb, shape=[-1, self.num_steps, self.hidden_size])if self.dropout is not None and self.dropout > 0.0:x_emb = self.dropout_layer(x_emb)# 使用LSTM网络,把每个句子转换为向量表示rnn_out, (last_hidden, last_cell) = self.simple_lstm_rnn(x_emb, (init_h, init_c))last_hidden = paddle.reshape(last_hidden[-1], shape=[-1, self.hidden_size])# 将每个句子的向量表示映射到具体的情感类别上projection = self.cls_fc(last_hidden)pred = F.softmax(projection, axis=-1)# 根据给定的标签信息,计算整个网络的损失函数,这里我们可以直接使用分类任务中常使用的交叉熵来训练网络loss = F.softmax_with_cross_entropy(logits=projection, label=label, soft_label=False)loss = paddle.mean(loss)# 最终返回预测结果pred,和网络的lossreturn pred, loss

 

(5)训练和预测

# 编写一个迭代器,每次调用这个迭代器都会返回一个新的batch,用于训练或者预测
def build_batch(word2id_dict, corpus, batch_size, epoch_num, max_seq_len, shuffle = True, drop_last = True):# 模型将会接受的两个输入:# 1. 一个形状为[batch_size, max_seq_len]的张量,sentence_batch,代表了一个mini-batch的句子。# 2. 一个形状为[batch_size, 1]的张量,sentence_label_batch,每个元素都是非0即1,代表了每个句子的情感类别(正向或者负向)sentence_batch = []sentence_label_batch = []for _ in range(epoch_num): #每个epoch前都shuffle一下数据,有助于提高模型训练的效果#但是对于预测任务,不要做数据shuffleif shuffle:random.shuffle(corpus)for sentence, sentence_label in corpus:sentence_sample = sentence[:min(max_seq_len, len(sentence))]if len(sentence_sample) < max_seq_len:for _ in range(max_seq_len - len(sentence_sample)):sentence_sample.append(word2id_dict['[pad]'])sentence_sample = [[word_id] for word_id in sentence_sample]sentence_batch.append(sentence_sample)sentence_label_batch.append([sentence_label])if len(sentence_batch) == batch_size:yield np.array(sentence_batch).astype("int64"), np.array(sentence_label_batch).astype("int64")sentence_batch = []sentence_label_batch = []if not drop_last and len(sentence_batch) > 0:yield np.array(sentence_batch).astype("int64"), np.array(sentence_label_batch).astype("int64")#训练预测
def train():step = 0sentiment_classifier = SentimentClassifier(embedding_size, vocab_size, num_steps=max_seq_len, num_layers=1)# 创建优化器Optimizer,用于更新这个网络的参数        optimizer = paddle.optimizer.Adam(learning_rate=0.01, beta1=0.9, beta2=0.999, parameters= sentiment_classifier.parameters()) sentiment_classifier.train()for sentences, labels in build_batch(word2id_dict, train_corpus, batch_size, epoch_num, max_seq_len):sentences_var = paddle.to_tensor(sentences)labels_var = paddle.to_tensor(labels)pred, loss = sentiment_classifier(sentences_var, labels_var)# 后向传播loss.backward()# 最小化lossoptimizer.step()# 清除梯度optimizer.clear_grad()step += 1if step % 100 == 0:print("step %d, loss %.3f" % (step, loss.numpy()[0]))# 我们希望在网络训练结束以后评估一下训练好的网络的效果# 通过eval()函数,将网络设置为eval模式,在eval模式中,网络不会进行梯度更新eval(sentiment_classifier)def eval(sentiment_classifier):sentiment_classifier.eval()# 这里我们需要记录模型预测结果的准确率# 对于二分类任务来说,准确率的计算公式为:# (true_positive + true_negative) / # (true_positive + true_negative + false_positive + false_negative)tp = 0.tn = 0.fp = 0.fn = 0.for sentences, labels in build_batch(word2id_dict, test_corpus, batch_size, 1, max_seq_len):sentences_var = paddle.to_tensor(sentences)labels_var = paddle.to_tensor(labels)# 获取模型对当前batch的输出结果pred, loss = sentiment_classifier(sentences_var, labels_var)# 把输出结果转换为numpy array的数据结构# 遍历这个数据结构,比较预测结果和对应label之间的关系,并更新tp,tn,fp和fnpred = pred.numpy()for i in range(len(pred)):if labels[i][0] == 1:if pred[i][1] > pred[i][0]:tp += 1else:fn += 1else:if pred[i][1] > pred[i][0]:fp += 1else:tn += 1# 输出最终评估的模型效果print("the acc in the test set is %.3f" % ((tp + tn) / (tp + tn + fp + fn)))

(全量数据,带新闻内容的)预测准确率在0.682,考虑优化特征向量或者更改其他模型!

参考资料:

https://blog.csdn.net/qq_42067550/article/details/106101183

https://paddleinference.paddlepaddle.org.cn/product_introduction/inference_intro.html

https://blog.csdn.net/xiaoxiaojie521/article/details/97240436

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/466687.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ken Thompson爷爷的经典复制自身代码程序 - Python版本(只用两行!)

Ken Thompson可是大牛人了&#xff01;不认识的人赶快Google/百度一下。Ken爷爷是Unix创始人&#xff0c;C语言鼻祖&#xff01;Ken 当时大学时候没游戏玩&#xff0c;就自己给自己出题目写程序&#xff0c;就有了这个复制自身代码的程序了&#xff0c;相当经典&#xff0c;当然…

显示日期的指令: date

1.显示日期的指令&#xff1a; date &#xff08;1&#xff09;参数&#xff1a; &#xff08;2&#xff09;实例 转载于:https://www.cnblogs.com/yfacesclub/p/8434369.html

聚类算法——Birch详解

1 原理 1.1 B树 &#xff08;1&#xff09;m路查找树 一棵m路查找树&#xff0c;它或者是一棵空树&#xff0c;或者是满足如下性质的树&#xff1a; 根最多有m棵子树&#xff0c;并具有以下结构&#xff1a; ,是指向子树的指针&#xff0c;是关键码&#xff0c; 在子树中所…

20180209-shutil模块

下面讲解shutil模块的相关操作: 1.shutil.copyfileobj(fsrc, fdst, length16*1024) 将fsrc文件内容拷贝到fdst文件中&#xff0c;length是指一次拷贝多少个字节 源码&#xff1a;def copyfileobj(fsrc, fdst, length16*1024):"""copy data from file-like objec…

输入一个数3256,将他从小到大输出,就是2356

这是知乎上面的一个题目&#xff0c;挺有意思&#xff0c;面试的时候应该是作为基础题&#xff0c;很多人不屑于看这样的题目&#xff0c;但是到自己些的时候&#xff0c;就会有很多问题。题目要求输入一个数3256&#xff0c;将他从小到大输出&#xff0c;就是2356分析输入的是…

深度学习之负荷分解——LSTM(一对多)任务场景

1.任务场景 以家庭为例&#xff0c;假设该家庭有m种电器和总负荷的曲线&#xff0c;其中&#xff0c;总负荷为,各个电器的负荷有&#xff08;m是电器个数&#xff0c;n是n个时刻&#xff09;。我们需要通过对总负荷曲线进行分解&#xff0c;识别出该负荷有几种电器同时运行实现…

这是我见过最秀的代码

这么秀的代码&#xff0c;你能写出来吗&#xff1f;技术主管来了都不好使&#xff0c;main函数里第一个函数必须是这个。佛祖保佑版&#xff1a;void godBless(void) { // _ooOoo_ // o8888888o // …

Cortex M3存储器映射

CortexM3存储器系统 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 CM3 只有一个单一固定的存储器映射。这一点极大地方便了软件在各种 CM3 单片机间的移植。 存储空间的一些位置用于调试组件等私有外设&#xff0c;这个地址段被称为“私有外设区”。私…

这些年,NLP常见的预训练模型剖析

“预训练-微调”&#xff08;pre-training and fine-tune&#xff09; 已经成为解决NLP任务的一种新的范式。基于预训练语言模型的词表示由于可以建模上下文信息&#xff0c;进而解决传统静态词向量不能建模“一词多义”语言现象的问题。传统的词向量有one-hot&#xff08;杜热…

Linux Ftrace 使用

Ftrace相关的文章如何利用ftrace精确跟踪特定进程调度信息1、Ftrace 是什么东西&#xff1f;Ftrace是一个直接内置在Linux内核中的跟踪工具。许多发行版在最近的发行版中已经启用了各种各样的Ftrace配置。Ftrace给Linux带来的好处之一是能够看到内核中发生了什么。因此&#xf…

Cortex M3寄存器组

寄存器组 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 1、通用目的寄存器R0~R7 R0-R7 也被称为低组寄存器。所有指令都能访问它们。它们的字长全是 32 位&#xff0c;复位后的初始值是不可预料的。 2、通用目的寄存器 R8-R12 R8-R12 也被称为高组寄…

深度学习-超参数调优

在机器学习中有很多调优的方式机器学习——超参数调优&#xff0c;深度学习中也存在同样的方式&#xff0c;接下来&#xff0c;介绍一下深度学习框架里边的自动调参模块。 1. 基于Tensorflow的Keras tuner 官方教程如下&#xff1a;Introduction to the Keras Tuner | Tens…

Paddle——常见的评估指标

在模型评估过程中&#xff0c;分类、回归、排序问题往往使用不同的指标进行评估。分类问题通常用准确率、召回率、精准率、F1值等指标进行评估&#xff1b;回归问题使用MSE、RMSE、R^2、MAPE等&#xff1b; 1 分类评估原理 1.1 准确率的局限性 我们经常接触的评价指标就是准…

大江大河,随笔观后感

我是「大江大河」的铁粉&#xff0c;非常喜欢这部剧&#xff0c;从这部剧里面能看到生活的一些影子。从这部剧里面也可以看到不同阶层的人们对待生活&#xff0c;对待理想&#xff0c;对待身边的朋友亲人的态度。—— 知乎热论「程开颜为什么输给了杨思申&#xff1f;」程开颜并…

Cortex M3内核架构

CortexM3内核架构 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 1、ARMCortex-M3处理器 Cortex-M3处理器内核是单片机的中央处理单元&#xff08; CPU&#xff09;。 完整的基于CM3的MCU还需要很多其它组件。在芯片制造商得到CM3处理器内核的使用授权…

NLP——序列标注之命名实体识别

1.概述 序列标注包括自然语言处理中的分词&#xff0c;词性标注&#xff0c;命名实体识别&#xff0c;关键词抽取&#xff0c;词义角色标注等。解决方案是NN模型&#xff08;神经网络模型&#xff09;CRF 命名实体识别&#xff08;Named Entity Recognition&#xff0c;简称N…

C语言验证6174数学问题

有意思的数学问题任意4位不完全一样的数字&#xff0c;能组合出的最大数字减去能组合出的最小数字&#xff0c;得到一个新的数字(3位数补0&#xff09;&#xff0c;重复以上操作&#xff0c;不超过7个循环&#xff0c;必然得到一个数&#xff1a;6174这个问题是之前发布的文章&…

Cortex-M3工作模式与异常

Cortex-M3工作模式与异常 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 一、工作模式 线程模式和手柄模式。 当处理器处在线程状态下时&#xff0c;既可以使用特权级&#xff0c;也可以使用用户级&#xff1b;另一方面&#xff0c; handler模式总是特…

自己动手实现一个malloc内存分配器 | 30图

对内存分配器透彻理解是编程高手的标志之一。如果你不能理解malloc之类内存分配器实现原理的话&#xff0c;那你可能写不出高性能程序&#xff0c;写不出高性能程序就很难参与核心项目&#xff0c;参与不了核心项目那么很难升职加薪&#xff0c;很难升级加薪就无法走向人生巅峰…