本项目主要围绕着特定领域知识图谱(Domain-specific KnowledgeGraph:DKG)融合方案:技术知识前置【一】-文本匹配算法、知识融合学术界方案、知识融合业界落地方案、算法测评KG生产质量保障讲解了文本匹配算法的综述,从经典的传统模型到孪生神经网络“双塔模型”再到预训练模型以及有监督无监督联合模型,期间也涉及了近几年前沿的对比学习模型,之后提出了文本匹配技巧提升方案,最终给出了DKG的落地方案。这边主要以原理讲解和技术方案阐述为主,之后会慢慢把项目开源出来,一起共建KG,从知识抽取到知识融合、知识推理、质量评估等争取走通完整的流程。
文本匹配任务在自然语言处理中是非常重要的基础任务之一,一般研究两段文本之间的关系。有很多应用场景;如信息检索、问答系统、智能对话、文本鉴别、智能推荐、文本数据去重、文本相似度计算、自然语言推理、问答系统、信息检索等,但文本匹配或者说自然语言处理仍然存在很多难点。这些自然语言处理任务在很大程度上都可以抽象成文本匹配问题,比如信息检索可以归结为搜索词和文档资源的匹配,问答系统可以归结为问题和候选答案的匹配,复述问题可以归结为两个同义句的匹配。
短文本匹配即计算两个短文本的相似度,通常分成无监督方式、有监督方式、有监督+无监督方式
常见的文本匹配算法如下表(简单罗列),按传统模型和深度模型简单的分为两类:
算法 | 类型 |
---|---|
Jaccord | 传统模型 |
BM25 | 传统模型 |
VSM | 传统模型 |
SimHash | 传统模型 |
Levenshtein | 传统模型 |
cdssm | 深度模型 |
arc-ii | 深度模型 |
match_pyramid | 深度模型 |
mvlstm | 深度模型 |
bimpm | 深度模型 |
drcn | 深度模型 |
esim | 深度模型 |
textmatching | 深度模型 |
bert-base | 深度模型 |
albert-base | 深度模型 |
albert-large | 深度模型 |
raberta | 深度模型 |
sbert | 深度模型 |
DiffCSE | 深度模型 |
ERNIE-Gram | 深度模型 |
文本表征:词袋模型(one-hot 、TF)、词向量预训练(word2vector、fasttext、glove)
相似度计算:余弦相似度、曼哈顿距离、欧氏距离、jaccard距离等
jaccard相似度是一种非常直观的相似度计算方式,即两句子分词后词语的交集中词语数与并集中词语数之比。
$\text { jaccard }=\frac{A \bigcap B}{A \bigcup B}$
def jaccard(list1, list2):
"""
jaccard相似系数
:param list1:第一句话的词列表
:param list2: 第二句话的词列表
:return:相似度,float值
"""
list1, list2 = set(list1), set(list2) #去重
intersection = list1.intersection(list2) # 交集
union = list1.union(list2) # 并集
Similarity = 1.0 * len(intersection) / len(union) #交集比并集
return Similarity
a.分词
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院
绵阳市 四 零 四 医院 / 四川 绵阳 404 医院
邓州市 人民 医院 / 南召县 人民 医院
b.去重求交集--并集
多伦县(交集) -- 内蒙古、锡林郭勒盟、多伦县、县医院、医院(并集)
医院(交集) -- 绵阳市、四、零、医院、四川、绵阳、404(并集)
人民、医院(交集) -- 邓州市、人民、医院、南召县(并集)
c.相似度
文本对 | 相似度 | 真实标签 |
---|---|---|
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院 | 1/5=0.2 | 1 |
绵阳市 四零四医院/四川 绵阳 404 医院 | 1/7 = 0.14 | 1 |
邓州市 人民 医院 / 南召县 人民 医院 | 2/4 = 0.5 | 0 |
一个句子转换为另一个句子需要的编辑次数,编辑包括删除、替换、添加,然后使用最长句子的长度归一化得相似度。
def Levenshtein(text1, text2):
"""
Levenshtein编辑距离
:param text1:字符串1
:param text2:字符串2
:return:相似度,float值
"""
import Levenshtein
distance = Levenshtein.distance(text1, text2)
Similarity = 1 - distance * 1.0 / max(len(text1), len(text2))
return Similarity
a.分词-计字数
内 蒙 古 锡 林 郭 勒 盟 多 伦 县 县 医 院 (14) / 多 伦 县 医 院(5)
绵 阳 市 四 零 四 医 院(8) / 四 川 绵 阳 4 0 4 医 院(9)
邓 州 市 人 民 医 院 (7) / 南 召 县 人 民 医 院(7)
b.计算编辑距离
内 蒙 古 锡 林 郭 勒 盟 多 伦 县 县 医 院 ------->删除内、蒙、古、锡、林、郭、勒、盟、县
绵 阳 市 四 零 四 医 院 ------->加 四、川
------->删除 市
------->替换 四(4)、零(0)、四(4)
邓 州 市 人 民 医 院 ------->替换邓(南)、 州(召)、 市(县)
文本对 | 距离 | 真实标签 |
---|---|---|
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院 | 0.357 | 1 |
绵阳市 四零四医院/四川 绵阳 404 医院 | 0.333 | 1 |
邓州市 人民 医院 / 南召县 人民 医院 | 0.571 | 0 |
先计算两句子的simhash二进制编码,然后使用海明距离计算,最后使用两句的最大simhash值归一化得相似度。
def simhash_(text1, text2):
"""
simhash相似度
:param s1: 字符串1
:param s2: 字符串2
:return: 相似度,float值
"""
from simhash import Simhash
text1_simhash = Simhash(text1, f=64) #计算simhash向量
text2_simhash = Simhash(text2, f=64) #计算simhash向量
distance = text1_simhash.distance(text2_simhash) #汉明距离
Similarity = 1 - distance / max(len(bin(text1_simhash.value)), len(bin(text2_simhash.value))) #相似度
return Similarity
a.分词
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院
绵阳市 四 零 四 医院 / 四川 绵阳 404 医院
邓州市 人民 医院 / 南召县 人民 医院
b.计算词权重(此处用tfidf计算,其他方法也可以)
| Text | Text | Text |
内蒙古5 | 锡林郭勒盟5 | 多伦县2 | 县医院5 | 多伦县7 | 医院1 | |||
---|---|---|---|---|---|---|---|---|
绵阳市3 | 四6 | 零3 | 四6 | 医院1 | 四川5 | 绵阳5 | 4045 | 医院1 |
邓州市7 | 人民4 | 医院1 | 南召县7 | 人民4 | 医院1 |
c.hash函数映射词向量
d.合并(将一段文本内的词向量进行累加)
e海明距离判断相似度
海明距离可以理解为:两个二进制串之间相同位置不同的个数。举个例子,1,1,1,0,0,0和1,1,1,1,1,1的海明距离就是3。
一句话概况其主要思想:对Query(待查询语句)进行语素解析,生成语素qi;然后,对于每个搜索结果D,计算每个语素qi与D的相关性得分,最后,将qi相对于D的相关性得分进行加权求和,从而得到Query与D的相关性得分。公式如下:
$\operatorname{Score}(Q, d)=\sum_i^n W_i \cdot R\left(q_i, d\right)$
Q表示Query,qi即Q分词后的每一个解析语素(对中文而言,我们可以把对Query的分词作为语素分析,每个词看成语素qi)。d表示一个搜索结果文档,Wi表示语素qi的权重,R(qi,d)表示语素qi与文档d的相关性得分。
判断一个词与一个文档的相关性的权重定义Wi方法有多种,较常用的是IDF。公式如下:
$\operatorname{IDF}\left(q_i\right)=\log \frac{N-n\left(q_i\right)+0.5}{n\left(q_i\right)+0.5}$
根据IDF的定义可以看出当很多文档都包含了qi时,qi的区分度就不高,因此使用qi来判断相关性时的重要度就较低。
$\operatorname{Score}(Q, d)=\sum_i^n I D F\left(q_i\right) \cdot \frac{f_i \cdot\left(k_1+1\right)}{f_i+k_1 \cdot\left(1-b+b \cdot \frac{d l}{a v g d l}\right)}$
求Score(qi,d)具体的公式可以参考文本相似度-BM25算法
其中
在做文本匹配的时候(如重复问题检测)可以尝试BM25的方法,但在搜索领域中,有时候搜索query和候选文档的长度是不一样甚至差距很大,所以BM25在计算相似性的时候需要对文档长度做一定的处理。
文本相似度-BM25算法
#Bm25
import math
import jieba
class BM25(object):
def __init__(self, docs):#docs是一个包含所有文本的列表,每个元素是一个文本
self.D = len(docs) #总文本数
self.avgdl = sum([len(doc)+0.0 for doc in docs]) / self.D #平均文本长度
self.docs = docs #文本库列表
self.f = [] # 列表的每一个元素是一个dict,dict存储着一个文档中每个词的出现次数
self.df = {} # 存储每个词及出现了该词的文档数量
self.idf = {} # 存储每个词的idf值
self.k1 = 1.5
self.b = 0.75
self.init()
def init(self):
for doc in self.docs: #对每个文本
tmp = {} #定义一个字典存储词出现频次
for word in doc:
tmp[word] = tmp.get(word, 0) + 1 # 存储每个文档中每个词的出现次数
self.f.append(tmp)
for k in tmp.keys():
self.df[k] = self.df.get(k, 0) + 1
for k, v in self.df.items():
self.idf[k] = math.log(self.D-v+0.5)-math.log(v+0.5) #计算idf
def sim(self, doc, index):
score = 0
for word in doc:
if word not in self.f[index]:
continue
d = len(self.docs[index])
score += (self.idf[word]*self.f[index][word]*(self.k1+1)
/ (self.f[index][word]+self.k1*(1-self.b+self.b*d
/ self.avgdl)))
return score
def simall(self, doc):
scores = []
for index in range(self.D):
score = self.sim(doc, index)
scores.append(score)
return scores
if __name__ == '__main__':
sents1 = ["多伦县医院", #数据库
"四川绵阳404医院",
"南召县人民医院"]
sents2 = ["内蒙古锡林郭勒盟多伦县县医院","绵阳市四零四医院","邓州市人民医院"]#待匹配文本
doc = []
for sent in sents1:
words = list(jieba.cut(sent))
doc.append(words)
print(doc)
s = BM25(doc)
print(s.f)
print(s.idf)
for k in sents2:
print(s.simall(jieba.lcut(k))) #打印相似度匹配结果
VSM算法的思路主要分为两步:
(1) 用向量表示句子,用向量表示句子的方法很多,简单的有onehot,词频法,基于语义的有word2vec/fastText/glove/bert/elmo等,本例中使用基于简单的词频的向量化方式。
(2)计算两向量的余弦距离(曼哈顿距离、欧几里得距离、明式距离、切比雪夫距离)得相似度。
#tfidf_余弦
def sim_vecidf(self, s1, s2):
"""词向量通过idf加权平均后计算余弦距离"""
v1, v2 = [], []
# 1. 词向量idf加权平均
for s in jieba.cut(s1):
idf_v = idf.get(s, 1)
if s in voc:
v1.append(1.0 * idf_v * voc[s])
v1 = np.array(v1).mean(axis=0)
for s in jieba.lcut(s2):
idf_v = idf.get(s, 1)
if s in voc:
v2.append(1.0 * idf_v * voc[s])
v2 = np.array(v2).mean(axis=0)
# 2. 计算cosine
sim = self.cosine(v1, v2)
return sim
a.句子向量化
a1.取句子对的唯一词元组
set(内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院) = (内蒙古 锡林郭勒盟 多伦县 县医院 医院)
set(绵阳市 四 零 四 医院 / 四川 绵阳 404 医院) = (绵阳市 四 零 医院 四川 绵阳 404 )
set(邓州市 人民 医院 / 南召县 人民 医院) = (邓州市 人民 医院 南召县)
a2.根据每个句子在元组中的词频建立向量表示
b.计算余弦距离
$\operatorname{Cos}(x 1, x 2)=\frac{x 1 \cdot x 2}{|x 1||x 2|}$
句子 | 距离 |
---|---|
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院 | 0.3535 |
绵阳市 四零四医院/四川 绵阳 404 医院 | 0.1889 |
邓州市 人民 医院 / 南召县 人民 医院 | 0.6666 |
常用做法是通过word2vec等预训练模型得到词向量,然后对文本做分词,通过embedding_lookup得到每个token对应的词向量,然后得到短文本的句向量。对两个文本的句子向量采用相似度计算方法如余弦相似度、曼哈顿距离、欧氏距离等。无监督方式取得的结果取决于预训练词向量的效果。
BERT是谷歌在2018年推出的深度语言表示模型,是关于语言理解的深度双向transformers的预训练模型,开启了预训练模型的新篇章。它可以学习文本的语义信息,通过向量形式的输出可以用于下游任务。也就说,它自己已经在大规模预料上训练好的参数,我们在用的时候只需要在这个基础上训练更新参数。bert模型可以解决多种自然语言处理问题,如单文本分类、语句对分类、序列标注等。在解决文本匹配任务时,有两种思路,第一种直接把文本匹配任务作为语句对分类任务,模型输入语句对,输出是否匹配的标签;第二种利用bert模型预训练文本对的上下文嵌入向量,再通过余弦相似度等相似度计算方法验证文本对是否匹配,在此基础上还有很多基于bert模型的变种,篇幅有限不做一一讲述。接下来简单介绍一下bert预训练文本嵌入+余弦相似度的算法框架。
a.首先使用大量公域文本数据对BERT模型进行预训练(或直接用谷歌预训练好的模型)
b.将文本直接输入模型
c.对模型输出的语义向量C,或中间隐层向量,计算余弦相似度,得到匹配结果。
基于深度学习的匹配算法种类繁多,如基于CNN网络、RNN网络、LSTM网络等及各种变种层出不穷,在此不一一列举实现。
传统的文本匹配方法主要关注文本间字与字,词与词的匹配关系,无法准确识别不同表达方式下不同文本的同一指向关系,即语义关系。因此在这一背景下,要对多源异构的海量地址数据进行匹配,传统的文本匹配方法已不再适用,深度学习方法大行其道。但深度学习方法也有自身的局限性,比如对海量文本和算力的高要求等,都使得深度学习方法的普适性大打折扣,因此没有最好的文本匹配算法,只有当前条件下最适合的文本匹配算法。
原文:《Learning a Similarity Metric Discriminatively, with Application to Face Verification》
* 用于解决类别很多(或者说不确定),然而训练样本的类别数较少的分类任务(比如人脸识别、人脸认证)
* 通常的分类任务中,类别数目固定,且每类下的样本数也较多(比如ImageNet)
提出了一种思路:将输入映射为一个特征向量,使用两个向量之间的“距离”(L1 Norm)来表示输入之间的差异(图像语义上的差距)。
基于上述思路设计了Siamese Network。每次需要输入两个样本作为一个样本对计算损失函数。
文中进行了一个衡量两张人脸的相似度的实验,使用了多个数据库,较复杂。siamese network现在依然有很多地方使用,可以取得state-of-the-art的效果。
分类问题:
文中提出的解决方案:
learn a similar metric from data。核心思想是,寻找一个映射函数,能够将输入图像转换到一个特征空间,每幅图像对应一个特征向量,通过一些简单的“距离度量”(比如欧式距离)来表示向量之间的差异,最后通过这个距离来拟合输入图像的相似度差异(语义差异)。
Siamese Network有两个结构相同,且共享权值的子网络。分别接收两个输入X1与X2,将其转换为向量Gw(X1)与Gw(X2),再通过某种距离度量的方式计算两个输出向量的距离Ew。
训练Siamese Network采用的训练样本是一个tuple (X1,X2,y)(X1,X2,y),标签y=0表示X1与X2属于不同类型(不相似、不重复、根据应用场景而定)。y=1则表示X2与X2属于相同类型(相似)。
LOSS函数的设计应该是
用L+(X1,X2)表示y=1时的LOSS,L−(X1,X2)表示y=0时的LOSS,则LOSS函数可以写成如下形式:
Lw(X1,X2)=(1−y)L−(X1,X2)+yL+(X1,X2)
简单来说:孪生体现在使用相同的编码器(sentence encoder),将文本转为高维向量。具体步骤为,有文本A和文本B分别输入 sentence encoder 进行特征提取和编码,将输入映射到新的空间得到特征向量u和v;最终通过u、v的拼接组合,经过下游网络来计算文本A和B的相似性
在上述孪生网络的基础上,得到特征u、v但是不直接计算向量相似度,而是通过注意力机制将两个文本进行信息交互,最后通过全连接层得到相似度。
代表的模型有ESIM,BIMPM等
以ESIM为例
这种模型参数多,并且使用了通用的语料库,能够获取到短文本之间隐藏的交互信息,效果较好。
简单来说用拼接的方法类似“单塔”,孪生网络的方法类似“双塔”,不完全准确后续会详细说明,预训练模型就不展开讲了,大家去官网或者多看几篇学术论文吧,BERT ERNIE。
无监督:直接相加得到句向量,不能很好的表达语义信息,并且词的位置信息没有得到体现,也不包含上下文的语义信息。
有监督学习:时间复杂度太高。可以将标准库中的句向量计算完成并存储。新的文本来临时,只需要解决用户问题即可,然后与存储在库中的标准问句进行距离度量。
可以使用BERT代替孪生网络的CNN或LSTM结构,获取更多语义信息的句向量,还可以通过蒸馏降低BERT模型的参数,节约时间成本。
文章链接:https://arxiv.org/pdf/1908.10084.pdf
论文代码:https://github.com/UKPLab/
为了让BERT更好地利用文本信息,作者们在论文中提出了如下的SBERT模型。SBERT沿用了孪生网络的结构,文本Encoder部分用同一个BERT来处理。之后,作者分别实验了CLS-token和2种池化策略(Avg-Pooling、Mean-Pooling),对Bert输出的字向量进一步特征提取、压缩,得到u、v。
关于u、v整合,作者提供了3种策略:
SBERT直接用BERT的原始权重初始化,在具体数据集微调,训练过程和传统Siamese Network类似。但是这种训练方式能让Bert更好的捕捉句子之间的关系,生成更优质的句向量。在测试阶段,SBERT直接使用余弦相似度来衡量两个句向量之间的相似度,极大提升了推理速度。
使用NLI和STS为代表的匹配数据集,在分类目标函数训练时,作者测试了不同的整合策略,结果显示“(u, v, |u-v|)”的组合效果最好。最重要的部分是元素差:(|u - v|)。句向量之间的差异度量了两个句子嵌入维度间的距离,确保相似的pair更近,不同的pair更远。
深度学习的本质是做两件事情:①表示学习 ②归纳偏好学习。对比学习(ContrastiveLearning)则是属于表示学习的范畴,它并不需要关注样本的每一个细节,但是学到的特征要使其能够和其他样本区分开。对比学习作为一种无监督表示学习方法,最开始也是在CV领域掀起浪潮,之后NLP跟进,在文本相似度匹配等任务上超过SOTASOTA。该任务主要是对文本进行表征,使相近的文本距离更近,差别大的文本距离更远。
NLP的对比学习算法下文将不详细讲述简单展示更多内容参考链接。
很多研究发现BERT表示存在问题:未经微调的BERT模型在文本相似度匹配任务上表现不好,甚至不如Glove?作者通过分析BERT的性质,如图:
在理论上BERT确实提取到了足够的语义信息,只是这些信息无法通过简单的consine直接利用。主要是因为:
针对以上问题,提出了BERT-Flow,基于流式生成模型,将BERT的输出空间由一个锥形可逆地映射为标准的高斯分布空间。
BERT-Whitening首先分析了余弦相似度为什么可以衡量向量的相似度:向量A 与B 的乘积等于A AA在B BB所在直线上投影的长度。将两个向量扩展到d维
$\cos (A, B)=\frac{\sum{i=1}^d a_i b_i}{\sqrt{\sum{i=1}^d ai^2} \sqrt{\sum{i=1}^d b_i^2}}$
$\text { 模的计算公式: }|A|=\sqrt{a_1^2+a_2^2+\ldots+a_n^2}$
上述等式的成立,都是在标准正交基(忘了的同学可以自行复习一下)的条件下,也就是说向量依赖我们选择的坐标基,基底不同,内积对应的坐标公式就不一样,从而余弦值的坐标公式也不一样。
所以,BERT的句向量虽然包含了足够的语义,但有可能是因为此时句向量所属的坐标系并非标准正交基,而导致用基于标准正交基的余弦相似度公式计算时效果不好。那么怎么知道具体用了何种基底呢?可以依据统计学去判断,在给向量集合选择基底时,尽量平均地用好每一个基向量,这就体现为每个分量的使用都是独立的、均匀的,如果这组基是标准正交基,那么对应的向量集应该表现出“各向同性”来,如果不是,可以想办法让它变得更加各向同性一写,然后再用余弦公式计算,BERT-Flow正是想到了“flow模型”的办法,而作者则找到了一种更简单的线性变换的方法。
标准化协方差矩阵
BERT-Whitening还支持降维操作,能达到提速和提效的效果。
★PCA和SVD差异分析:PCA可以将方阵分解为特征值和特征向量,SVD则可以分解任意形状的矩阵。
https://arxiv.org/pdf/2105.11741.pdf
美团技术团队提出了基于对比学习的句子表示迁移方法——ConSERT,主要证实了以下两点:
①BERT对所有的句子都倾向于编码到一个较小的空间区域内,这使得大多数的句子对都具有较高的相似度分数,即使是那些语义上完全无关的句子对。我们将此称为BERT句子表示的“坍缩(Collapse)”现象。
②BERT句向量表示的坍缩和句子中的高频词有关。当通过平均词向量的方式计算句向量时,高频词的词向量将会主导句向量,使之难以体现其原本的语义。当计算句向量时去除若干高频词时,坍缩现象可以在一定程度上得到缓解。
为了解决BERT存在的坍缩问题,作者提出了句子表示迁移框架:
对BERT encoder做了改进,主要包括三个部分:
*①数据增强模块,作用于embedding层,为同一文本生成不同的编码。
数据增强效果:Token Shuffle > Token Cutoff >> Feature Cutoff ≈ Dropout >> None
$L{i, j}=-\log \frac{\exp \left(\operatorname{sim}\left(r_i, r_j\right) / \tau\right)}{\sum{k=1}^{2 N} 1_{k \neq i} \exp \left(\operatorname{sim}\left(r_i, r_k\right) / \tau\right)}$
N:Batchsize,2N表示2种数据增强方式,sim():余弦相似度函数,r:句向量,τ:实验0.08−0.12最优
除了无监督训练之外,作者还提出了三种进一步融合监督信号的策略:
参考链接:https://blog.csdn.net/PX2012007/article/details/127614565
前几节讲述了对比学习的原理和几种基于 Bert 的方式获取句子向量,例如 BERT-flow和 BERT-whitening 等,对预训练 Bert 的输出进行变换从而得到更好的句子向量。后面将通过 ①构造目标函数 ②构建正负例 的对比学习方法训练模型,取得SOTA的效果。
SimCSE是有大神陈丹琦发表的《Simple Contrastive Learning of Sentence Embeddings》,简单高效
SimCSE包含无监督(图左部分)和有监督(图右部分)两种方法。实线箭头代表正例,虚线代表负例。
创新点在于使用Dropout对文本增加噪音。
1.正例构造:利用Bert的随机Dropout,同一文本经过两次Bert enconder得到不同的句向量构成相似文本。
2.负例构造:同一个Batch中的其他样本作为负例被随机采样。
1.正例:标注数据
2.负例:同Batch内的其他样本
https://arxiv.org/abs/2106.14448
Dropout虽然可以防止模型训练中的过拟合并增强鲁棒性,但是其操作在一定程度上会使训练后的模型成为一种多个子模型的组合约束。SimCSE就是希望Dropout对模型结果不会有太大影响,也就是模型输出对Dropout是鲁棒的。所以,“Dropout两次”这种思想是可以推广到一般任务的,这就是R-Drop(Regularized Dropout),由微软亚洲研究院和苏州大学提出的更加简单有效的正则方法。
同样的输入,同样的模型,经过两个 Dropout 得到的将是两个不同的分布,近似将这两个路径网络看作两个不同的模型网络。基于此,这两个不同的模型产生的不同分布而这篇文章的主要贡献就是在训练过程中不断拉低这两个分布之间的KL 散度。由于KL 散度本身具有不对称性,作者通过交换这两种分布的位置以间接使用整体对称的KL 散度,称之为双向KL 散度。
https://arxiv.org/abs/2109.04380
SimCSE构建正负例时存在两个两个缺点:
ESimCSE针对以上问题做了相应的改进:
https://arxiv.org/pdf/2201.04337v1.pdf
Prompt Learning比较火热,号称NLP的第四范式,
作者分析了造成embedding bias的原因,除了token frequency是造成bias的原因,作者又提出了:subwords,case sentitive
图中不同词频token的分布情况,颜色越深代表词频越高,我们可以看出词频高的token,分布比较紧凑,词频低的token,分布较分散。作者输出这个图像还有一个目的是他提出各向异性(anisotropy)和偏差(bias)是不相关的,各向异性不是导致偏差的原因。
Embedding bias意思是映射分布被一些不相关的信息所干扰,是可以用降维的方式去可视化的。
更多细节参考原论文,Prompt效果就不赘述了,百度开发的UIE模型在NLP就很强大!
https://arxiv.org/abs/2201.05979
SNCSE同样是由微软团队提出,主要是针对以上方法存在的问题:当前对比学习的数据增强方式,获取的正样本都极为相似,导致模型存在特征抑制,即模型不能区分文本相似度和语义相似度,并更偏向具有相似文本,而不考虑它们之间的实际语义差异。
为了减轻特征抑制,该论文提出了通过软负样本结合双向边际损失的无监督句子嵌入对比学习方法。其中,软负样本,即具有高度相似,但与原始样本在语义上存在明显的差异的样本。双向边际损失,即通过扩大原始样本与正例和原始样本与软负例之间的距离,使模型更好地学习到句子之间的语义差别。
软负样本构造:为原文本添加显示的否定词。
https://arxiv.org/pdf/2204.10298.pdf
结合句子间差异的无监督句子嵌入对比学习方法——DiffCSE主要还是在SimCSE上进行优化(可见SimCSE的重要性),通过ELECTRA模型的生成伪造样本和RTD(Replaced Token Detection)任务,来学习原始句子与伪造句子之间的差异,以提高句向量表征模型的效果。
其思想同样来自于CV领域(采用不变对比学习和可变对比学习相结合的方法可以提高图像表征的效果)。作者提出使用基于dropout masks机制的增强作为不敏感转换学习对比学习损失和基于MLM语言模型进行词语替换的方法作为敏感转换学习「原始句子与编辑句子」之间的差异,共同优化句向量表征。
在SimCSE模型中,采用pooler层(一个带有tanh激活函数的全连接层)作为句子向量输出。该论文发现,采用带有BN的两层pooler效果更为突出,BN在SimCSE模型上依然有效。
参考链接:https://blog.csdn.net/PX2012007/article/details/127696477
SimCSE以来几种比较重要的文本增强式的对比学习算法,按时间顺序,理论上应该是距离越近的算法效果越好,但使用时,还是要结合具体的业务场景,算法没有好坏,还是用看怎么用。对于有些内容,可能叙述的不是很细致或是需要一定的知识铺垫,感兴趣的同学可以针对性的研读论文和辅助其他资料。当然,算法层出不穷,更新很快,后续出现比较重要的对比学习算法。
特征工程
根据IF A=B and A=C THEN B=C的规则,对正样本做了扩充增强。
根据IF A=B and A!=C THEN B!=C的规则,对负样本做了扩充增强。
在对负样本进行扩充后, 正负样本比例从原始的1.4:1, 变成2.9:1。 所以又对负样本进行了下采样, 是的正负样本比例1:1。
使用开源包synormise的效果不太好, 后面可以尝试使用公开医学预料训练word2vec模型来做同义词替换(时间问题, 没有尝试)。
随机删除,随机替换, 随机交换
句式比较短, 随机删除更短。
很多query仅仅相差一个单词, 随机替换改变语义。
多数属于问句, 随机交换改变了语义。
在预训练模型的基础上通过对抗训练增强模型的泛化性能。
目前所训练的模型中:
小模型中BERT-wwm-ext表现是最好的,
大模型中RoBERTa-large-pair表现最好。
在现有的资源和模型上, 对单模型的参数寻优到达一个天花板,线上最高的分数为0.9603。后面开始探索多模型融合。
将不同类型的预训练模型作为基模型进行多模型融合。基模型的挑选准则基于单模型的线上提交效果,从不同类型的单模型中挑选线上表现最好的参数, 重新训练融合。
基模型:
BERT-wwm-ext + FGM
RoBERTa-large-pair + FGM
Ernie(BaiDu)+ FGM
模型融合策略使用的是averaging, 为了降低差别比较小的模型对结果的影响,采用sigmoid反函数的方式进行ensemble。
关于对抗训练在NLP中的作用,引用大佬的一句话叫缘,妙不可言~
文本长度:训练集和验证集分布类似,大都集中在10-20个字
标签分布
总体思路
采用kfold交叉验证(右边的划分方式)
•利用全部数据,获得更多信息
•降低方差,提高模型性能
小模型同时加入CHIP2019数据训练
模型 | 特点 | 权重 | 加入外部句对数据 |
---|---|---|---|
BERT-wwm-ext | 全词Mask | 1 | YES |
Ernie-1.0 | 对词、实体及实体关系建模 | 1 | YES |
RoBERTa-large-pair | 面向相似性或句子对任务优化 | 1 | NO |
对称扩充、传递扩充(注意要保持原来的分布,否则会过拟合)
#代码来自苏剑林bert4keras
def adversarial_training(model, embedding_name, epsilon=1):
"""给模型添加对抗训练
其中model是需要添加对抗训练的keras模型,embedding_name
则是model里边Embedding层的名字。要在模型compile之后使用。
"""
if model.train_function is None: # 如果还没有训练函数
model._make_train_function() # 手动make
old_train_function = model.train_function # 备份旧的训练函数
# 查找Embedding层
for output in model.outputs:
embedding_layer = search_layer(output, embedding_name)
if embedding_layer is not None:
break
if embedding_layer is None:
raise Exception('Embedding layer not found')
# 求Embedding梯度
embeddings = embedding_layer.embeddings # Embedding矩阵
gradients = K.gradients(model.total_loss, [embeddings]) # Embedding梯度
gradients = K.zeros_like(embeddings) + gradients[0] # 转为dense tensor
# 封装为函数
inputs = (model._feed_inputs +
model._feed_targets +
model._feed_sample_weights) # 所有输入层
embedding_gradients = K.function(
inputs=inputs,
outputs=[gradients],
name='embedding_gradients',
) # 封装为函数
def train_function(inputs): # 重新定义训练函数
grads = embedding_gradients(inputs)[0] # Embedding梯度
delta = epsilon * grads / (np.sqrt((grads**2).sum()) + 1e-8) # 计算扰动
K.set_value(embeddings, K.eval(embeddings) + delta) # 注入扰动
outputs = old_train_function(inputs) # 梯度下降
K.set_value(embeddings, K.eval(embeddings) - delta) # 删除扰动
return outputs
model.train_function = train_function # 覆盖原训练函数
写好函数后,启用对抗训练只需要一行代码
adversarial_training(model, 'Embedding-Token', 0.5)
更多方案就不一一展开了,参考下方链接:
https://tianchi.aliyun.com/notebook/101626
https://tianchi.aliyun.com/notebook/101648
https://tianchi.aliyun.com/notebook/101648
参考链接:
https://tianchi.aliyun.com/competition/entrance/231776/forum
https://tianchi.aliyun.com/notebook/102057
在前面技术知识下可以看看后续的实际业务落地方案和学术方案
关于图神经网络的知识融合技术学习参考下面链接:PGL图学习项目合集&数据集分享&技术归纳业务落地技巧[系列十]
从入门知识到经典图算法以及进阶图算法等,自行查阅食用!
文章篇幅有限请参考专栏按需查阅:NLP知识图谱相关技术业务落地方案和码源
方案链接:https://blog.csdn.net/sinat_39620217/article/details/128614951
方案链接:https://blog.csdn.net/sinat_39620217/article/details/128673963
方案链接:https://blog.csdn.net/sinat_39620217/article/details/128674429
方案链接:https://blog.csdn.net/sinat_39620217/article/details/128674929
方案链接:https://blog.csdn.net/sinat_39620217/article/details/128675199
论文资料链接:两份内容不相同,且按照序号从小到大重要性依次递减
知识图谱实体对齐资料论文参考(PDF)+实体对齐方案+特定领域知识图谱知识融合方案(实体对齐)
知识图谱实体对齐资料论文参考(CAJ)+实体对齐方案+特定领域知识图谱知识融合方案(实体对齐)
方案链接:https://blog.csdn.net/sinat_39620217/article/details/128675698
本项目主要围绕着特定领域知识图谱(Domain-specific KnowledgeGraph:DKG)融合方案:技术知识前置【一】-文本匹配算法、知识融合学术界方案、知识融合业界落地方案、算法测评KG生产质量保障讲解了文本匹配算法的综述,从经典的传统模型到孪生神经网络“双塔模型”再到预训练模型以及有监督无监督联合模型,期间也涉及了近几年前沿的对比学习模型,之后提出了文本匹配技巧提升方案,最终给出了DKG的落地方案。这边主要以原理讲解和技术方案阐述为主,之后会慢慢把项目开源出来,一起共建KG,从知识抽取到知识融合、知识推理、质量评估等争取走通完整的流程。
大家好,又见面了,我是你们的朋友全栈君。java中存在两个随机函数,它们分别来自java.long.Math.random()和java.util.Random();其中前者的适用范围比较小,完全可以被后者取代。一、java.lang.Math.random()方法的用法①、方法类型: publicstaticdoublerandom();此方法是一个无参,double类型返回值的公开静态方法。返回一个大于0的double类型数据,该值大于等于0.0且小于1.0,返回的是一个伪随机选择数,在该范围内(几乎)均匀分布。例如:publicclassTestRandom{ publicstaticvoidmain(String[]args){ intsum=0; while(true){ floata=(float)Math.random(); System.out.println(a); sum++; if(sum==10) break; } } }复制二、 java.util.Random类用法①、该类的构造方法: Random():构造
本文作者:Ashton[1]0x01Luna价格崩了,借贷应用躺枪了近期Luna是圈内最热门的话题了,其价格从最高的100多美金,直接跌倒不到0.000001美金,后面又涨了100多倍到0.000X美金。当大家都在分析Luna本身时,币安链上最大的借贷应用Venus却出事了,有人存入大量的Luna,借走了大量资产,导致平台坏账。Venus使用业界头部预言机Chainlink[2]作为价格源,当时Luna的价格在0.01美金左右,但从Chainlink拿到的报价却停留在了0.107美金。Chainlink的报价居然停止工作了0x02Chainlink的回应按照Chainlink官方回应,停止报价的原因是因为Luna的价格波动超出了正常范围,触发了内置的熔断机制,这是协议抗风险的一种措施。只是这个抗风险的措施却带来了实实在在的损失,也是滑稽。官方的第二个回应,是让Luna价格源404(https://data.chain.link/bsc/mainnet/crypto-usd/luna-usd)了。0x03熔断代码分析Chainlink的价格都是最终由一个叫聚合器(Aggregator)的
远程开发有很多种类型,有直接线上环境的云端IDE,文件、编译、测试都可以在云端的IDE实现,跟本地的IDE差别不大,这种方案本质上就是一个webconsole。另外的类型就是通过同步本地开发文件夹里的文件,至少实现文件从本地-->远程同步的功能,这种方案本质上就是通过sftp等方法,同步文件,真正的编译还是在远程做的,当然也可以在本地编译好之后直接同步上传到远程运行。本文主要讲的是第二种方式,Goland上的配置跟普通的JetBrains其他产品如IDEA无异,就是通过配置Deployment来实现的。 Step1 点击+号,增加SFTP配置 Step2 配置Connection的参数,就是普通的SSH的参数,注意host和port要填对了。 Step3 配置Mapping,这里的Mapping指的是远程文件夹和本地文件夹的映射关系。在图中,我会把本地的/Users/runzhliu/tencent/glog和远程的/data/k8s给映射起来。 Step4 测试同步。 上图的文件夹树是通过Goland内置的remotehostbrowser查看的,可以配置个快捷键来快
昨天发了关于线程更新UI的一些内容,其中关于View的一些问题还是比较重要的。我们再来回顾下,并提出一些问题。Activity从创建到我们看到界面,发生了哪些事首先是通过setContentView加载布局,这其中创建了一个DecorView,然后根据然后根据activity设置的主题(theme)或者特征(Feature)加载不同的根布局文件,最后再通过inflate方法加载layoutResID资源文件,其实就是解析了xml文件,根据节点生成了View对象。流程图:加载布局流程其次就是进行view绘制到界面上,这个过程发生在handleResumeActivity方法中,也就是触发onResume的方法。在这里会创建一个ViewRootImpl对象,作为DecorView的parent然后对DecorView进行测量布局和绘制三大流程。流程图:绘制流程Activity、PhoneWindow、DecorView、ViewRootImpl的关系?PhoneWindow是Window的唯一子类,每个Activity都会创建一个PhoneWindow对象,你可以理解它为一个窗口,但不是真
转译自HowToCalculateAgeFromDateOfBirthInMySQL-Querychat,中文转载,请注明出处。使用SQL语句计算年龄,在事务处理和日期计算中,较为常见。MySQL提供了许多日期函数,可以自由发挥。本文中看我们尝试SQL年龄计算——组件MySQL没有开箱即用的工具,用于计算年龄。所以,这也阻挡不了我们求知的热情。工具不够,自己来凑。没有直接的函数,我们就用给的函数,组装出来一个。说白了,年龄就是啷个日期差。当前日期,减去生日。那就是年龄。MySQL已经提供的函数,下面介绍一下:CURDATE()–返回当前日期TIMESTAMPDIFF()–计算时间差,差值单位自定义这俩函数就够了,日期差,获取年差值,月差值,或者其他。CURDATE()返回MySQL服务器运行时间。无需传参,调用如下:CURDATE()TIMESTAMPDIFF()调用格式如下:TIMESTAMPDIFF(unit,begin_date,end_date)复制单位unit决定了返回数值。unit的可选项如下:YEARQUARTERMONTHWEEKDAYHOURMINUTESECONDM
本文来源于公众号「肥朝」前言17年的时候,因为一时冲动没把持住(当然最近也有粉丝叫我再冲动一把再更新一波),结合面试题写了一个系列的Dubbo源码解析.目前公众号大部分粉丝都是之前的粉丝,这里不过多介绍.根据我的面试经验而言,能在简历上写上原理、源码等关键词的,是非常具备核心竞争力的.上周和一个公众号粉丝交流面试情况如下面试的时候,把源码一波分析,令面试官虎躯一震!在一阵前戏过后,以为接下来无非就是身体的一顿抽搐一切变得索然无味,不料面试官来了句令剧情发生了反转"你对Dubbo源码这么熟悉,那请问你使用的时候,有没有遇到什么坑"我擦,毫无准备的他,菊花顿时一紧!此时就面临唬住了50K,唬不住就只能5K的局面,慌了!论如何反杀相信大家面试都遇到过类似问题,因为源码解析网上很多,很多人"考前突击"一下,但是遇到喜欢问细节的面试官,终究难逃法眼,无处遁形.遇到这个问题,我们如何反杀一波?那么我就从一次聊天记录说起,毕竟只有关注肥朝公众号,拥有真实场景的源码实战(非常重要),遇到这类问题,才不至于出现猛虎落泪的情形真实场景描述那么我们把业务相关去掉,抽取
接下来我们来补充之前AFURLResponseSerialization这一块是如何解析数据的@protocolAFURLResponseSerialization<NSObject,NSSecureCoding,NSCopying> /** Theresponseobjectdecodedfromthedataassociatedwithaspecifiedresponse. @paramresponseTheresponsetobeprocessed. @paramdataTheresponsedatatobedecoded. @paramerrorTheerrorthatoccurredwhileattemptingtodecodetheresponsedata. @returnTheobjectdecodedfromthespecifiedresponsedata. */ -(nullableid)responseObjectForResponse:(nullableNSURLResponse*)response data:(nullableNSData*)d
15万颗GitHubStar,57个涵盖人工智能、移动开发、小程序、架构、系统等多个前沿技术领域的开源项目——这是6月26日上午,腾讯移动互联网事业群总经理刘昕,作为腾讯开源顾问和Linux基金会董事,在LC3(LinuxCon+ContainerCon+CloudOpen)国际开源盛会上,介绍的腾讯部分开源成果。在过去六年里,腾讯开源坚持做好开发者体验,从内部开源到对外开源,实现从开源新兵到开源社区一员大将的跨越。 腾讯开源始于2010年,在开放战略之下,“开放、共享、合力开发”的研发模式开始在内部推行。至今,已有超过8000个优质项目在腾讯内部跨团队、跨部门、跨业务地被广泛运用。这为腾讯外部开源打下了坚实基础。2016年至今,腾讯不断将内部开源出来的优质项目在GitHub上发布,腾讯开源逐步进入快节奏时代。其中仅2017年一年,就开源将近20个项目。其中不乏世界排名前十的前端开发工具WeUI,阅文集团、科大讯飞等公司都广泛使用的微服务框架TARS,可以快速建立大规模机器学习平台的AI开源项目Angel等受国际社区高度认可的优秀开源项目。然而,要深度融入开源社区,不只需项目发布,更需
巴洁如 腾讯研究院高级研究员 过去的五十年中,技术创新已成为金融服务产业转型升级过程中的重要驱动力。从半导体、中央处理器、个人电脑、互联网、智能设备、移动通信,到云计算、大数据、物联网、生物识别、区块链,技术创新不断涌现并日益成熟,将引领金融服务领域的下一轮创新浪潮。此前,金融技术创新集中体现在应用层面,对基础设施的改造作用较为局限。 有观点认为,技术的出现实质上是将纸面操作转换成半电子化过程,而整个逻辑还是以纸质文件为基础的。而区块链技术之所以在金融业界引起高度关注,正是因为其有望对金融服务的基础设施产生变革性影响。本文从区块链技术的产生、兴起和基本原理出发,简要探讨区块链技术应用于传统跨境支付业务模式的潜在效益和存在问题。区块链技术溯源 作为比特币的底层技术,区块链技术(也称为分布式账本技术DLT)近年来在科技和金融领域受到广泛关注。2008年,化名为“中本聪”的个人(或组织)发布了一种点对点现金系统及其基础协议,2009年诞生了名为“比特币”的加密货币。与法定货币不同,比特币没有集中的发行方,完全由网络节点计算生成。在经历了大幅度的价格波动之后,对比特币等虚拟货币的讨论逐渐回归
Instagram宣布将其MonkeyType工具开源。它是通过运行时跟踪类型自动将类型注释添加到您的Python3代码的工具。授权协议:BSD开发语言:Python操作系统:跨平台该公司拥有数百名工程师,编译了超过一百万行代码。它不断地在生产环境中添加新的代码,所以公司需要一种方法来使开发人员更容易阅读和理解代码,同时减少潜在的错误代码。“在MonkeyType的帮助下,我们已经在代码库中对三分之一的功能进行了注释,而且我们已经看到了类型检查会捕获更多的错误。”Instagram基础设施团队工程师CarlMeyer在一篇文章中写道。使用在MonkeyType可以告诉我们任何有用的东西之前,我们需要让它跟踪一些函数调用。最简单的方法是monkeytyperun在MonkeyType跟踪下运行任何Python脚本。例如,您可以在MonkeyType下轻松运行您的测试套件:$monkeytyperunruntests.py或者:monkeytyperun`whichpytest`当你的测试运行时,MonkeyType检查每个函数调用的参数类型和返回/yield类型,并将它们记录在数据库中。
SublimeTextSFTP连接AmazonEC2实现步骤[^2]SFTP配置参考文献SublimeTextSFTP连接AmazonEC2 SublimeText3正式版发布了,全平台IDE都换成这个。今天终于有点时间来研究下如何使用pem连接EC2ServerEC2Server会提供给你一个.pem的key,但是单纯用这个Key无法直接连接解决办法很简单:将.pem的引用改成对.ppk的引用1实现步骤2 下载安装PuTTYTypeofkeytogenerate,选择RSA如果你在用旧版本的PuTTy,那么就选择SSH-2RSA点击Load,选择你的.pemSFTP配置 { "type":"sftp", "sync_down_on_open":true, "host":"xx.xxx.xx.xx.xx",//hostip "user":"xxxxxx",//username "remote_path":"/"
一个5年工作经验的小伙伴,去面某东被问到MyBatis何时使用一级缓存,何时使用二级缓存?去之前还特地复习了MyBatis的相关知识,想着自己用MyBatis用得比较熟练了,竟然在这道题上翻车了。今天,我给大家来分享一下MyBatis的缓存机制。1、设计思想Mybatis里面设计了两级缓存来提升数据的检索效率,避免每次数据的访问都需要去查询数据库。先来看一级缓存,它是SqlSession级别的缓存,也叫本地缓存,因为每个用户在执行查询的时候都需要使用SqlSession来执行,为了避免每次都去查数据库,MyBatis把查询出来的数据保存到SqlSession的本地缓存中,后续的SQL如果命中缓存,就可以直接从本地缓存读取。那如果想要实现跨SqlSession级别的缓存?一级缓存就无法实现了,因此,MyBatis引入了二级缓存。当多个用户在查询数据的时候,只要有任何一个SqlSession拿到了数据就会放入到二级缓存里面,其他的SqlSession就可以从二级缓存加载数据。ENTERTITLE2、原理分析接下来,我给大家详细分析一下MyBatis缓存机制的实现原理。先来看一级缓存的实现原
上周上线完之后,平台频繁出现问题,从服务器查看pod状态为Running 但是从日志中查看就是直接被killed 检查过nginx日志、数据库等未发现异常由上图可以看出最后直接就是被killed下意识的我会以为是程序运行超过了所指定的Xmx参数,但是平台运行的情况我还是了解的,之前即便访问量大的是的也是个别服务或者数据库压力大,不会导致这两天无规律性质的死掉服务,几乎什么服务都可能会进行被killed 我尝试调整过启动脚本Xmx参数但是没用,一样还是会被killed之前也处理过关于pod启动异常的问题,然后我去检查各个节点运行资源情况:free-h#查看运行内存 df-h#查看磁盘空间 top#查看CPU复制并未发现异常这里插句话如果磁盘空间超过85%,会导致docker启动回收垃圾机制,删除你的镜像一类的,会导致你的pod启动异常 运行内存不足也会导致pod启动异常 无异常,就更令我感到诧异并无助 解决问题最怕的就是没发现问题然后先经过治理表面先及时解决平台运行问题kubectldeletepodXXX复制经过重启pod,然后重新运行没问题的,不过过一会指不定又是什么服务进行kill
#print()#input()#len()#type()#open()#tuple()#list()#int()#bool()#set()#dir()#id()#str()#print(locals())#返回本地作用域中的所有名字#print(globals())#返回全局作用域中的所有名字#global声明全局变量可以修改#nonlocal声明局部变量可以修改#迭代器.__next__()#next(迭代器)等同于上面的迭代器.__next__()#迭代器=可迭代的.__iter__()#迭代器=iter(可迭代的)等同于上面的可迭代的.__iter__()复制 range(10) range(1,11) print('__next__'indir(range(1,11,2)))复制 #dir查看一个变量拥有的方法复制 print(dir([])) print(dir(1)) help help(str)复制 #callable()判断是不是函数,是否可被调用复制 print(callable(print))#True是不是函数,能否被调用 a=1 print(cal
1.引言 前面文章的测试案例都用到了集搜客Gooseeker提供的规则提取器,在网页抓取工作中,调试正则表达式或者XPath都是特别繁琐的,耗时耗力,工作枯燥,如果有一个工具可以快速生成规则,而且可以可视化的即时验证,就能把程序员解放出来,投入到创造性工作中。 之前文章所用的例子中的规则都是固定的,如何自定义规则再结合提取器提取我们想要的网页内容呢?对于程序员来说,理想的目标是掌握一个通用的爬虫框架,每增加一个新目标网站就要跟着改代码,这显然不是好工作模式。这就是本篇文章的主要内容了,本文使用一个案例说明怎样将新定义的采集规则融入到爬虫框架中。也就是用可视化的集搜客GooSeeker爬虫软件针对亚马逊图书商品页做一个采集规则,并结合规则提取器抓取网页内容。 2.安装集搜客GooSeeker爬虫软件 2.1.前期准备 进入集搜客官网产品页面,下载对应版本。我的电脑上已经安装了Firefox38,所以这里只需下载爬虫。 2.2安装爬虫 打开Firefox–>点击菜单工具–>附加组件–>点击右上角附加组件的工具–>选择从文件安装附加组件->选中下载好的爬虫xp
自动驾驶汽车功能安全的国际标准是iso26262,而自动驾驶预期功能安全(SOTIF)的国际标准是iso21448。这两者之间的关系如何呢? 参考资料: 1、揭秘ISO21448,它是自动驾驶行业的新风向标?,https://baijiahao.baidu.com/s?id=1627688549067562514&wfr=spider&for=pc 2、系统架构图和硬件架构图,有什么区别?,https://mp.weixin.qq.com/s?__biz=MzUyNDc0Njc3Mw==&mid=2247486020&idx=1&sn=cbb7a057c10d056f93c87a8c7a0c9e3a&chksm=fa29e33ecd5e6a283221b9edd62691dbfdf97c781cf15014fbbdd27cc986e43631e5f8027cc0&scene=132#wechat_redirect
前言 当前手机使用成为互联网主流,每天手机App产生大量数据,学习爬虫的人也不能只会爬取网页数据,我们需要学习如何从手机APP中获取数据,本文就以豆果美食为例,讲诉爬取手机App的流程 环境准备 python3 fiddler 一款支持桥接模式的安卓虚拟机(本文使用夜神模拟器) 需要准备的知识有: requests的使用 mongodb的使用 fiddler抓包工具的基本操作 线程池ThreadPoolExecutor的基本使用 项目开始 我们项目的目标是将豆果美食App中所有的菜谱都抓取下来到我们的本地数据库中 本文不再讲解fiddler、安卓模拟器、以及某些python第三方库的安装,不会的同学可以百度,非常简单的操作 我们抓取的流程大概就是 安卓模拟器使用代理连接至fiddler 打开安卓模拟器进行操作 分析fiddler抓到的数据 使用python模拟数据给服务器发送request请求得到响应数据 使用多线程抓取并在本地保存至数据库 1)安卓模拟器使用代理连接至fiddler 打开fiddler,进行设置打开最上方菜单栏中的Tools菜
/*程序:求关节点(无向图) *分析: *一个low数组,low[u]=min(low[v]|vinn) *若low[v]>=low[u],则u是关节点 *步骤: *1.初始状态:一个无向图 *1,数据结构:vector<int>邻接表,vis数组,low数组,count序号全局变量 *2,初始化: *2.过程: *1,判断根节点是否是关节点 1,若生成树的根节点有两可或两颗以上的子树,则此根节点必为关节点 *2,深度优先搜索遍历邻接表,更新序号数组 *3,后序遍历 *4,判断:若low[v]>=low[u],则u是关节点,输出,否则更新low[u]值 */ #include<iostream> #include<vector> #include<algorithm> #include<cstring> usingnamespacestd; #defineMAX1001 vector<int>g[MAX]; intvis[MAX]; intlow[MAX]; intcnt; intans; bool
1最长公共子序列 给定两个字符串text1和text2,返回这两个字符串的最长公共子序列的长度。如果不存在公共子序列,返回0。一个字符串的子序列是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 例如,"ace"是"abcde"的子序列,但"aec"不是"abcde"的子序列。两个字符串的公共子序列是这两个字符串所共同拥有的子序列。 输入:text1="abcde",text2="ace" 输出:3 解释:最长公共子序列是"ace",它的长度为3。复制 输入:text1="abc",text2="def" 输出:0 解释:两个字符串没有公共子序列,返回0。方法:动态规划复制 classSolution{ publicintlongestCommonSubsequence(Stringtext1,Stringtext2){ intm=text1.length(); intn=text2.length(); int[][]dp=newint[m+1][n+1]; f