浅析文本相似度

  在自然语言处理(Natural Language Processing, NLP)中,经常会涉及到如何度量两个文本的相似度问题。在诸如对话系统(Dialog system)和信息检索(Information retrieval)等的问题中,如何度量句子或者短语之间的相似度尤为重要。为方便对知识的梳理,写下这篇博客对部分传统方法、词向量、深度学习方法进行总结,遗漏之处还请大家补充。
  度量文本相似度包括如下三种方法:一是基于关键词匹配的传统方法,如N-gram相似度;二是将文本映射到向量空间,再利用余弦相似度等方法;三是深度学习的方法,如基于用户点击数据的深度学习语义匹配模型DSSM,基于卷积神经网络的ConvNet,以及目前state-of-art的Siamese LSTM等方法。
  随着深度学习的发展,文本相似度的各种方法已经逐渐不再是基于关键词匹配的传统方法,而转向深度学习,目前结合向量表示的深度学习使用较多。

1 基于关键词匹配

1.1 N-gram 相似度

  基于N-Gram模型定义的句子(字符串)相似度是一种模糊匹配方式,通过两个长得很像的句子间的“差异”来衡量相似度。
  N-Gram相似度的计算是指按长度N切分原句得到词段,也就是原句中所有长度为N的子字符串。对于两个句子S和T,则可以从共有子串的数量上去定义两个句子的相似度。
  

S i m i l a r i t y = | G N ( S ) | + | G N ( T ) | 2 | G N ( S ) G N ( T ) |

  其中, G N ( S ) G N ( T ) 分别表示字符串S和T中N-Gram的集合,N一般取2或3。字符串距离越近,它们就越相似,当两个字符串完全相等时,距离为0。

1.2 Jaccard 相似度

  Jaccard相似度的计算相对简单,原理也容易理解。就是计算两个句子之间词集合的交集和并集的比值。该值越大,表示两个句子越相似,在涉及大规模并行运算的时候,该方法在效率上有一定的优势,公式如下:
  

J ( A , B ) = | A B | | A B |

  其中 0 J ( A , B ) 1

2 基于向量空间

  关于如何表达词意目前有三种方法,第一种的代表就是WordNet,它提供了一种词的分类资源但是缺少了词之间的细微区别,同时它也很难计算词之间的相似度;第二种就是Discrete representation,如One-hot representation,它的向量维度和词典的长度相同,因此向量维度可能十分高,同时由于向量之间本身正交,无法计算词之间的相似度;第三种就是Distributed representation,基本想法就是将每个词映射为一个固定长度的短向量(相对于one-hot representation而言),所有这些词构成一个词向量空间,每一个向量视为空间中的一个点,在这个空间引入“距离”,就可以根据词之间的距离来判断它们之间的相似性,代表方法如word2vec, LDA等。

2.1 Word2vec

  早在2003年Bengio等人在《A neural probabilistic language model》中提出的神经网络语言模型(NNLM)就用到了重要的工具-词向量,只是在这个模型中,目标是生成语言模型,词向量只是一个副产品。
  Mikolov et al. (2013)提出的word2vec核心思想是认为词的意思可以从它频繁出现的上下文信息中体现(A word’s meaning is given by the words that frequently appear close-by)。
  Word2Vec又可分为两种结构,一种是CBOW,利用窗口内的上下文预测中心词;另一种是Skip-gram,利用中心词预测窗口内的上下文。
  以Skip-gram为例,其结构如下:
  这里写图片描述
  目标函数(Object function)
  这里写图片描述
  下面是Skip-gram的整体框架图,其中W为V*N维矩阵,W’为N*V维矩阵,这两个矩阵都有V个向量,表示词表中的每个词都有2个向量,一个是作为中心词的向量,一个是作为上下文时的向量,这两个向量都是模型中的参数,最终的词向量可以将这两个向量拼接或者直接相加。
  这里写图片描述
  当然这里对Word2Vec的介绍十分粗略,限于篇幅有限,很多细节和trick等无法展现,以后单独写一篇介绍word2vec的博客。

2.2 TF-IDF

  TF-IDF的核心思想是:在一篇文章中,某个词语的重要性与该词语在这篇文章中出现的次数成正相关,同时与整个语料库中出现该词语的文章数成负相关.
  TF(term frequency):词频,某一个给定的词语在该文件中出现的频率,表示一个词语与该文章的相关性。这个数字是对词数(term count)的归一化,以防止它偏向长的文件。
  IDF(inverse document frequency):逆向文件词频,表示一个词语出现的普遍程度。某一特定词语的idf,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取以10为底的对数得到。
  一篇文章中某个词语的重要程度,可以标记为词频和逆向文件词频的乘积,

t f i d f = t f i d f

  通过计算出文档中每个词的TF-IDF值,然后结合相似度计算方法(一般采用余弦相似度)就可以计算两个文档的相似度。采用TF-IDF的前提是“文章的词语重要性与词语在文章中出现的位置不相关”。

2.3 LSA

//TODO

2.4 相似度计算

  相似度就是比较两个事物的相似性。一般通过计算事物的特征之间的距离,如果距离小,那么相似度大;如果距离大,那么相似度小。
  问题定义:有两个对象X、Y,都包括N维特征, X = ( x 1 , x 2 , x 3 , . . , x n ) , Y = ( y 1 , y 2 , y 3 , . . , y n ) ,计算X和Y的相似性。常用的方法如下:

2.4.1 欧式距离

  欧氏距离是最常用的距离计算公式,衡量的是多维空间中各个点之间的绝对距离,当数据很稠密并且连续时,这是一种很好的计算方式。
  因为计算是基于各维度特征的绝对数值,所以欧氏度量需要保证各维度指标在相同的刻度级别,如在KNN中需要对特征进行归一化。
  

d = i = 1 n ( x i y i ) 2

2.4.2 曼哈顿距离

  曼哈顿距离也称为城市街区距离(City Block distance)。
  

d = i = 1 n | x i y i |

2.4.3 余弦相似度

  余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。相比距离度量,余弦相似度更加注重两个向量在方向上的差异,而非距离或长度上。
  

c o s θ = i = 1 n x i y i i = 1 n x i 2 i = 1 n y i 2

2.4.4 其他

  其他的相似度计算方法包括皮尔森相关系数、一般化的闵可夫斯基距离(当p=1时为曼哈顿距离,当p=2时为欧式距离)、汉明距离等。

3 基于深度学习

3.1 深度学习

  随着深度学习在图像和语音识别中取得不错的进展,近些年深度学习也开始应用于自然语言处理的不同应用中。
  语义相似性匹配问题已经逐渐从人工设计特征转向分布式表达和神经网络结构相结合的方式。(Recent work has moved away from hand crafted features and towards modeling with distributed representations and neural network architectures)

3.2 DSSM

  DSSM是一种深度学习语义匹配模型,在检索场景下,利用用户的点击数据来训练语义层次的匹配(The proposed deep structured semantic models are discriminatively trained by maximizing the conditional likelihood of the clicked documents given a query using the clickthrough data)。
  DSSM利用点击率来代替相关性,点击数据中包含大量的用户问句和对应的点击文档,这些点击数据将用户的问题和匹配的文档连接起来。
  下图就是利用DNN的DSSM结构。DNN的输入是用户问句和对应文档的高维未归一化向量(如图中的500K维),DNN的输出是一个低维的语义特征空间向量(如图中的128维)。
这里写图片描述
  上图中的Multilayer non-linear projection采用了非线性的多层神经网络,利用 t a n h 作为激活函数,同时问句和文档的神经网络共享参数,并在DNN的输出层利用cosine similarity计算问句和文档之间的语义相关性。

R ( D , Q ) = c o s i n e ( y Q , y D ) = y Q T y D | | y Q | | | | y D | |

  其中 y Q y D 表示DNN输出层问句和文档的低维语义向量,然后通过softmax得到条件概率分布。
  
   Word hashing
  原始的问句或者文档是一个高维的向量,如向量的维度可能是词表的大小,当输入这些高维度的向量时神经网络可能无法训练。为了使得神经网络更好的训练,论文提出了减少DNN输入层向量维度的 Word hashing方法,也即上图中的word hashing层。
  word hashing是一种基于字符的n-gram方法。如对good一词,首先添加开始和结尾的标记得到#good#,然后将该词分解为字符n-grams(如采用trigram: #go,goo,oog,od#),最后这个词就会被这些n-grams向量表示。
  一种可能存在的问题是认为不同词的词向量冲突(collision)。但是经过作者的统计,如在500k维的one-hot编码基础上,采用字符级trigram时,向量维度可以降到30k维,且冲突率仅为0.0044%。

  DSSM的训练中,神经网络层的参数 w b 都是通过最大化点击数据的条件概率得到,公式如下:
  

P ( D | Q ) = e x p ( γ R ( D , Q ) ) D D e x p ( γ R ( Q , D ) )

  其中, γ 是softmax函数的平滑因子,是训练中的一个经验值。在实际训练中,对于每一个问句共有5个文档对应,其中一个是点击文档,其余4个是随机选择的非点击文档。
  文档的相关性采用人工标注,共分为5个分数(0-4),其中4分表示最相关,0分表示最不相关。
  
  DSSM的优点在于1.直接利用了用户的点击数据;2. 得到的结果可以直接排序;但是缺点在于没有利用上下文信息。
  
  DSSM的扩展还包括CDSSM、DSSM-LSTM等,其中CDSSM在一定程度上弥补了上下文缺失的问题,结构上将DNN替换成CNN。
  DSSM-LSTM中使用LSTM记录上下文。

3.3 ConvNet

  ConvNet通过精心设计卷积神经网络,结合不同规格的卷积神经网络的差异性度量句子的相似度。(Proposing an elaborate convolutional network variant which infers sentences similarity by integrating various difference across many convolutions at various scale)
  在论文《Multi-Perspective Sentence Similarity Modeling with Convolutional Neural Networks》中,采用“Siamese”(孪生)CNN结构,分别对两个句子建模,然后利用一个句子相似度测量层计算句子相似度,最后通过一个全连接层输出softmax相似度得分。整体的网络结构如下所示:
  这里写图片描述
  可以看到,一个句子首先被转化为嵌入矩阵(embedding matrix),然后输入卷积-池化层(论文提出使用2种卷积方式和3种池化操作,也即论文题目“多视角”的由来)得到处理后的句子向量,为更好计算句子之间的相似度,分别对向量不同的输出结果计算其相似性,最终将相似度向量输入全连接层得到相似性分数,与标签值相比较。
  这里写图片描述
  1.卷积层
  论文提出两种卷积方式,一种是holistic filter,如上图左上角 B l a c k A ,其尺寸为 [ w s , embed_size, 1, num_filter_A],其中 w s 表示滑动窗口的宽度,取值为1,2或者无穷大(句子长度),这种卷积核考虑了窗口内词向量的所有维度,输出尺寸为[sentence_len- w s +1,1,num_filter_A],相当于n-gram特征的提取。
  第二种是pre-dimension filter,如上图左下角 B l a c k B ,它是在embed_size的每一个维度上分别进行卷积,注意这里不是采用[ w s ,1]对整个句子进行卷积,而是使用embed_size个小卷积核分别对每一行进行卷积,也就是说如第一行的小红框(卷积核),只对该行卷积得到右边的一列,第二行的小红框得到右边的第二列,以此类推。采用该种卷积核,作者给出的解释是有助于充分的提取特征信息,尽管作者也直说这种方式很难给出符合人直观上的解释。
  2. 池化层
  卷积层之后得到的上图中间部分,然后通过不同的池化层,这里作者采用了三种池化层方式:max-pooling,min-pooling和mean-pooling。对卷积得到的结果直接在列的维度上取最大、最小和平均值,将卷积层的输出减少了一个维度。对于 B l a c k A ,池化层采用三种池化操作,对于 B l a c k B 采用max,min两种池化操作。
  定义 B l a c k A B l a c k B 如下:
  { g r o u p A ( w s a , p , s e n t ) : p m a x , m i n , m e a n }, w s a [ 1 , 3 ] ,
  { g r o u p B ( w s b , p , s e n t ) : p m a x , m i n }, w s b = 1 , 2
  3.不同的卷积窗口
  对于 B l a c k A ,对应于n-gram特征提取, w s 的取值为1,2,无穷大(相当于句子长度,直接得到全局特征),对于 B l a c k B w s 的取值为1,2。
  这里写图片描述
  4.相似度计算
  相似度计算时,采用了两种计算函数,分别由余弦距离,L1距离和L2距离组成:
  这里写图片描述
  论文提到,每个句子都会经过多个卷积-池化层( B l a c k A 有9个, B l a c k B 有4个),如果在计算相似度时,不考虑它们之间的对应关系直接在最后生成的向量上计算相似度则会丢失很多有用的信息。为了使得向量局部间比较和计算有效,需要考虑几个方面:
  (1)是否来自同一个building block;(2)是否来自相同卷积窗口大小下的卷积结果;(3)是否来自同一个pooling层;(4)是否来自相同的feature map。
  最终比较句子中的相应部分时,需要至少满足以上两个条件,为了识别句子中哪些部分需要参与相似度度计算,文中提供了两种算法。
  如下,算法1和算法2为句子表征向量的两种计算方法,其中算法1用在 B l a c k A 上,算法2则用在 B l a c k A B l a c k B 上,两种算法都是针对相同类型的pooling和block的输出做局部比较。
  这里写图片描述
  文中针对上述算法提出一个简单的例子解释:
  这里写图片描述
  从图中可以看出,算法1对于绿色的框,其实就是每一个句子对应每一种池化方式而言,经过卷积层之后得到一个num_filters * 3(3指窗口的大小),也即上图中绿色的框宽度应该是3,纵向为num_filters。伪代码6-9就是遍历输出的每一行,计算句子相似性(即max到 max),然后对每种池化方式做相同处理。因此绿色框也不止是代表第一行,下面每一行都有。
  红色框代表算法2的2-9行。同样没有画全,应该对同一池化的每个句子的 w s 1 w s 2 比较。
  5.全连接层
  基于句子的相似度计算之后,得到相应的相似度向量;然后这组向量之后连接一个全连接层,最后softmax对应输出,如果是计算相似度度量值,可以用softmax输出类别概率。

  模型参数:
  1. w s [ 1 , 3 ] w s 取无穷大;
  2. Word embedding,采用300维的Glove word embedding,同时在MSRP任务中也结合了POS embedding 和Paragram Vectors;
  3.在MSRP时采用5-折交叉验证的方式,对模型参数进行微调,微调好的模型参数将会用在另外两个数据集上;
  4.使用tanh激活函数,优化算法采用随机梯度下降;
  5.学习率为0.01,正则化参数 λ 为0.0001
  
  总体来看,这个模型的复杂度还是很高的,而且很多地方作者在论文中也表示都是经验得来的,卷积核在垂直方向的计算也没有特别直观的解释, B l a c k B 为何只采用max-pooling和min-pooling,而不采用mean-pooling等问题。

3.4 Skip-thoughts Vectors

  Skip-thoughts的核心思想:将Word2vec中的skip-gram方法从词的层面扩展到句子的层面,利用seq2seq模型预测输入语句的上下句。(Extending the skip-gram approach of word2vec from the word to sentence.This model feeds each sentences into an RNN encoder-decoder(with GRU activations)which attempts to reconstructs the immediately preceding the following sentences)
  论文提到,在之前的各类监督方法中,模型通过确定的标签作为优化目标更新参数虽然能取得不错的效果,但是只能适用于不同的任务。
  模型的结构如下:
  这里写图片描述
  模型在一个连续的文本语料(小说)上进行训练,通过Encoder端将词转化为句子向量,然后在Decoder端结合Encoder端的句子向量生成上下文语句。
  Decoder端和Encoder端都采用了GRU,因为GRU不仅在序列模型任务上和LSTM表现一样好,而且在概念理解和计算上都要比LSTM简单。
  Decoder端是一个基于Encoder端输出 h i 神经网络语言模型(NNLM),其中一个Decoder用来生成下一个句子 s i + 1 ,另一个Decoder用于生成上一个句子 s i 1
  对于一个句子组 ( s i 1 , s i , s i + 1 ) ,优化的目标函数是上下句的对数似然之和,整个训练集上的目标函数就是对所有的句子组加和。
  这里写图片描述
  对于这样一个模型,最大的问题在于如何把有限的词扩展到任意的词或句。论文中采用的方法是学习一种映射,将一个模型中的词表示映射到另外一个模型中。具体的操作是把CBOW中预训练得到的词向量映射到Encoder端的词空间。最终将词汇量扩展到1百万词。
  
  训练好的Skip-thoughs模型,会把Encoder端作为特征提取器,对所有的句子提取Skip-thoughs向量,得到的这些向量表示可以用在不同的任务中,如语义相似度计算。

3.5 Tree-LSTM

  Tree -LSTM的核心思想:将对语序敏感的标准LSTM序列推广为树状结构的网络拓扑图。(Generalizing the order-sensitive chain-structure of standard LSTMs to tree-structure network topologies)
  标准的LSTM仅仅考虑了句子的序列信息,但是在自然语言中句法结构能够自然的将词结合成短语或句子,因此作者在论文中提出了两种利用句法结构信息的LSTM扩展结构:The Child-Sum Tree-LSTM和N-arr Tree-LSTM。
  和标准的LSTM结构一样,每个Tree-LSTM(记为 j )单元也包括输入门 i j 和输出门 o j ,记忆单元 c j 和隐藏状态 h j 。不同点是,Tree-LSTM的各个门向量(gate vector)和记忆单元(memory cell)的更新依赖于其子单元的可能状态,此外,Tree-LSTM的每个子单元 k 都有一个遗忘门 f j k ,这使得Tree-LSTM能选择性的合并子单元的信息。
  这里写图片描述
  正如标准的LSTM,每个Tree-LSTM单元都有一个输入向量 x j ,每个 x j 是句子中词的向量表示。
  Child-Sum Tree-LSTMs
  对于一棵结构树,假设 C ( j ) 代表节点 j 的子节点集合。
  这里写图片描述
  在上述公式中,我们可以将参数矩阵理解为Tree-LSTM单元和输入 x i 及子节点的隐藏层状态 h k 的向量相关性。如在依存句法分析树中,当输入的词在语义上相对重要时,模型可以学习到参数矩阵W^{(i)}使得输入门 i j 的值接近1(即“打开”);当输入的是相对不重要的词时,输入门 i j 的值接近0(即“关闭”)。
  当Child-Sum Tree-LSTM应用到依存句法分析时,称为Dependency Tree-LSTM
  N-arr Tree-LSTMs
  在N-arr Tree-LSTMs树结构中,子节点的个数最多可以是N个,并且子节点是有序的。
  对于每个结点 j ,记其第 k 个子节点的隐藏状态和记忆单元分别为 h j k c j k ,则N-arr Tree-LSTMs结构如下:
  这里写图片描述
  在N-ary Tree LSTM结构中,每个子节点 k 都有独立的参数矩阵,这使得N-ary Tree LSTM比Child-Sum Tree-LSTM能更好的从其子节点学习到一些细节。
  Constituency Tree-LSTM可以看做是N-ary Tree-LSTM在N为2的特例。

  论文中提到基于Tree-LSTM结构的两种模型,一种是Tree-LSTM Classification,另一种是Semantic Relatedness of Sentence Pairs
  (1)Tree-LSTM Classification
  对树中结点 j 分类时,结合该结点的子树使用softamx分类器进行预测,分类器采用结点的隐状态 h k 作为输入:
  这里写图片描述
  损失函数采用负的对数似然损失函数,并且利用L2正则化。其中 m 是训练集的样本数量,上标 k 表示第k个标签结点, λ 是L2正则化参数。
  这里写图片描述
  (2)Semantic Relatedness of Sentence Pairs
  在语义相似度计算中,首先把经过Tree-LSTM模型后的句子对表示为 h L h R ,接着结合句子对 ( h L , h R ) 的距离和角度,利用神经网络来预测相似度得分。
  这里写图片描述
  其中, r T = [ 1 2 . . . K ] 和绝对值函数都是基于单个值运算,其中,距离度量方法 h X h + 都是基于实际经验,两者结合时会比单个度量方式更好。
  在进行预测时,为了归一化输出值,定义如下分布p,满足 y = r T p
  这里写图片描述
  其中, 1 i K 。损失函数采用带正则化项的KL散度,优化目标是让标签值 p 和预测值 p ^ θ 更接近, m 表示训练句子对的数量,上标 k 表示第k个句子对。
  这里写图片描述
  
  对于Dependency Tree-LSTM模型,最大的问题在于训练数据的收集十分困难并且句法分析结果也不是在所有语言中都有。

3.6 Siamese LSTM/Manhattan LSTM/MaLSTM

  Siamese LSTM之前简要介绍一下Siamese Network。Siamese Network用来度量数据之间的相似性,两组数据(文本,图像等)同时输入到一个神经网络中,并经由这个神经网络转化为N*1维的向量,此后会通过一个数值函数(如余弦相似度)计算这两个向量的距离,通过得到的距离来度量原始输入数据的相似性。
  这里写图片描述
  在标准的Siamese Network中,两侧的神经网络需要使用相同的网络结构和参数;同时在进行梯度更新前,需要先对两侧的梯度平均;两个输出向量需要满足相似或者不相似的关系。
  
  Siamese LSTM
  The model is applied to access for semantic similarity between sentences,a siamese adaptation of the LSTM network for labeled data comparised of pairs of variable-length sequences.
  Siamese LSTM结构大体与Siamese Network相似,其网络结构如下所示:
   这里写图片描述
  模型的输入:首先对句子分词,然后将词转化为预先训练好的Word2vec向量,并按序列输入网络。(按论文的意思,模型的Word2vec是利用外部数据语料训练的,并且词向量维度为300维)
  模型的输出:输入句子对的相似度分数。由于模型采用 e x 打分函数且x是输出向量差的1向量范数,因此输出的得分区间为[0,1]。所以在具体的实现时候,对模型的输出进行转换,将得分区间转化为[1,5]之间。(论文采用SICK数据集,句子对的相似度得分在[1,5]之间,由10个不同的人标注)
  上述模型中, h 3 ( a ) h 4 ( b ) 分别是两个LSTM模型的最后隐藏层状态,得到的向量作为特征空间中句子的embedding,随后在特征空间中计算向量相似度,以此来推断句子对的语义相似度。根据作者的意思,这两个LSTM可以是共享参数的,也可以是不共享参数的。
  模型的损失函数采用MSE(均方损失函数),优化算法采用Adelta,为防止训练中梯度爆炸,采用gradient clipping。
  
  关于该模型有两点值得注意:
  1.LSTM,在Siamese Network中采用孪生LSTM,是因为LSTM能够解决RNN的长期依赖问题,通过使用记忆单元(memory cell),LSTM能够储存更长输入序列的信息。当然对于特别长的句子而言,标准的LSTM能力也是有限的,对于长度超过30的长句子,通过模型得到的最终隐藏层状态,占据比重较大的还是后面的词,前面的词基本消失,因此需要Attention。
  2. Manhattan Similarity。在度量相似性的时候,采用了曼哈顿距离而不是欧式距离,根据论文和博客 的观点,一方面用Word2vec训练出来的词,存在大量欧式距离相等的情况,如果用L2范数去衡量,存在语义丢失的情况,而cosine similarity适合度量向量维度特别大的情况,因此采用Manhattan距离最合适;另一方面,采用L2范数会存在梯度消失的问题,在训练的早期,L2范数会错误的认为两个语义不相关的句子相似因为采用欧式距离时的梯度消失问题(This is because during early stages of training,a L2-based model is unable to correct errors where it erroneously believes semantically different sentence to be nearly identical due to vanishing gradients of the Euclidean distance)。

3.7 Others

//TODO

4 参考文献

  1. NLP点滴——文本相似度
  2. 常用的相似度计算方法原理及实现
  3. 基于Simase_LSTM的计算中文句子相似度经验总结与分享
  4. 原创:Siamese LSTM解决句子相似度以及扩展(理论篇)
  5. 论文《Leaning Deep Structured Semantic Models for Web Search using Clickthrough Data
  6. 论文《Siamese Recurrent Architecture for learning Sentence Similarity》
  7. CNN在句子相似性建模的应用–模型介绍篇
  8. 论文《Multi-Perspective Sentence Similarity Modeling with Convolutional Neural Networks
  9. 卷积神经网络(CNN)在句子建模上的应用
  10. 《Improved Semantic Representations From Tree-Structured Long Short-Term Memory Networks》阅读笔记
  11. 论文《Improved Semantic Representations From Tree-Structured Long Short-Term Memory Networks》
  12. 基于TreeLSTM的情感分析
  13. 论文《Skip-Thought Vectors》
  14. cips2016+学习笔记︱简述常见的语言表示模型(词嵌入、句表示、篇章表示)

猜你喜欢

转载自blog.csdn.net/qq_28031525/article/details/79596376