略多

略多 查看完整档案

北京编辑中国科学院大学  |  计算机技术 编辑  |  填写所在公司/组织填写个人主网站
编辑

可傻了!

个人动态

略多 发布了文章 · 1月19日

工具——pdf在线转换并保存

import requests
import time

class PDF2Word():
    def __init__(self):
        self.machineid = 'ccc052ee5200088b92342303c4ea9399'
        self.token = ''
        self.guid = ''
        self.keytag = ''
    def produceToken(self):
        url = 'https://app.xunjiepdf.com/api/producetoken'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0',
            'Accept': 'application/json, text/javascript, */*; q=0.01',
            'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'X-Requested-With': 'XMLHttpRequest',
            'Origin': 'https://app.xunjiepdf.com',
            'Connection': 'keep-alive',
            'Referer': 'https://app.xunjiepdf.com/pdf2word/',}
        data = {'machineid':self.machineid}
        res = requests.post(url,headers=headers,data=data)
        res_json = res.json()
        if res_json['code'] == 10000:
            self.token = res_json['token']
            self.guid = res_json['guid']
            print('成功获取token')
            return True
        else:
            return False
    def uploadPDF(self,filepath):
        filename = filepath.split('/')[-1]
        files = {'file': open(filepath,'rb')}
        url = 'https://app.xunjiepdf.com/api/Upload'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0',
            'Accept': '*/*',
            'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
            'Content-Type': 'application/pdf',
            'Origin': 'https://app.xunjiepdf.com',
            'Connection': 'keep-alive',
            'Referer': 'https://app.xunjiepdf.com/pdf2word/',}
        params = (
            ('tasktype', 'pdf2word'),
            ('phonenumber', ''),
            ('loginkey', ''),
            ('machineid', self.machineid),
            ('token', self.token),
            ('limitsize', '2048'),
            ('pdfname', filename),
            ('queuekey', self.guid),
            ('uploadtime', ''),
            ('filecount', '1'),
            ('fileindex', '1'),
            ('pagerange', 'all'),
            ('picturequality', ''),
            ('outputfileextension', 'docx'),
            ('picturerotate', '0,undefined'),
            ('filesequence', '0,undefined'),
            ('filepwd', ''),
            ('iconsize', ''),
            ('picturetoonepdf', ''),
            ('isshare', '0'),
            ('softname', 'pdfonlineconverter'),
            ('softversion', 'V5.0'),
            ('validpagescount', '20'),
            ('limituse', '1'),
            ('filespwdlist', ''),
            ('fileCountwater', '1'),
            ('languagefrom', ''),
            ('languageto', ''),
            ('cadverchose', ''),
            ('pictureforecolor', ''),
            ('picturebackcolor', ''),
            ('id', 'WU_FILE_1'),
            ('name', filename),
            ('type', 'application/pdf'),
            ('lastModifiedDate', ''),
            ('size', ''),)
        res= requests.post(url,headers=headers,params=params,files=files)
        res_json = res.json()
        if res_json['message'] == '上传成功':
            self.keytag = res_json['keytag']
            print('成功上传PDF')
            return True
        else:
            return False
    def progress(self):
        url = 'https://app.xunjiepdf.com/api/Progress'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0',
            'Accept': 'text/plain, */*; q=0.01',
            'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'X-Requested-With': 'XMLHttpRequest',
            'Origin': 'https://app.xunjiepdf.com',
            'Connection': 'keep-alive',
            'Referer': 'https://app.xunjiepdf.com/pdf2word/',}
        data = {
            'tasktag': self.keytag,
            'phonenumber': '',
            'loginkey': '',
            'limituse': '1'}
        res= requests.post(url,headers=headers,data=data)
        res_json = res.json()
        if res_json['message'] == '处理成功':
            print('PDF处理完成')
            return True
        else:
            print('PDF处理中')
            return False
    def downloadWord(self,output):
        url = 'https://app.xunjiepdf.com/download/fileid/%s'%self.keytag
        res = requests.get(url)
        with open(output,'wb') as f:
            f.write(res.content)
            print('PDF下载成功("%s")'%output)
    def convertPDF(self,filepath,outpath):
        filename = filepath.split('/')[-1]
        filename = filename.split('.')[0]+'.docx'
        self.produceToken()
        self.uploadPDF(filepath)
        while True:
            res = self.progress()
            if res == True:
                break
            time.sleep(1)
        self.downloadWord(outpath+filename)

使用:

if __name__=='__main__':
    pdf2word = PDF2Word()
    pdf2word.convertPDF('002.pdf','')

结果:
image.png
瑕疵:
image.png
如上图,容易把一些段落解析成表格,没有直接使用wps来的精确。

查看原文

赞 0 收藏 0 评论 0

略多 发布了文章 · 1月12日

哈工大LTP模型——Mac上的安装与使用

一、安装

1、一上午半下午的时间都花在了安装上,最后弄明白了ltp和pyltp不一样,而且成熟的版本是3.4.0。
2、(就是这么菜)弄明白了github的一些项目上面还能查看历史的release,正儿八经的3.4.0就是从release里边找着的
复杂性:

python3.6(3.7不行)
对编译器也有要求
还要修改一些配置

资源:
在mac上安装pyltp把ltp这个目录替换到pyltp里
xcrun:error解决方法
ltp网站:里边提供有模型的下载
pyltp使用方法
LTP命名实体识别标注集:对照实体识别之后的结果

查看原文

赞 0 收藏 0 评论 0

略多 发布了文章 · 1月6日

知识图谱描边1.2——NER&CRF&数据标注

named_entity_recognition

命名实体识别公司实操——详细步骤(raw数据+标注+数据集处理+crf)
项目的github地址

任务目标


从一些工厂、单位的日常检查日志中,识别出隐患设备实体和地点实体,如下表:

隐患日志隐患地点隐患设备
1轧钢部一轧反吹压力表未校验轧钢部反吹压力表
2铸管4.0施工现场多处气瓶间距不符合要求铸管、4.0施工现场气瓶
………………
121煤气职业危害告知牌检测数据未更新煤气职业危害告知牌

ps:上表的顺序仅是实例展示,不代表selected_data.xlsx中的真实情况。
实验步骤:
1、将每一句话用BIO标注方式标注
2、写dataloader
3、使用crf模型训练
下面我们介绍详细的标注工具及步骤


数据标注:

  • 想要的训练数据集展示如下:

image.png
善其事而先利其器,采用YEDAA这个python开源工具包来标注,运行后界面长这样:
image.png

  • 使用时几点注意:

1、操作方法是用英文写的,基本能涵盖你的使用要求,花几分钟时间建一个txt文件练练就会了;
2、目前YEDDA(2020.11)只支持Python2,我用conda创建了一个py2环境;
3、支持直接导入文件(open按钮),我使用的是txt格式;
4、支持导出形式设置,标注完成后点击Export按钮即可输出,我们会得到一个.anns文件;
5、可设置快捷键命令,一键标注光标内文本
6、荧光笔标记的标签如果挨着,一定要避免重叠,一个字符的重叠都不可以,否则会出现如下错误:
image.png

数据集处理

  • 数据分析:

数据量只有121句,划分为10:1(110对train数据。11对val数据,也就是用来测试的。

  • 流程:

①把121个sentence和tag对以(s,t)的形式加载到内存(load_dataset)
②用固定的随机种子打乱顺序
③划分列表,再分别存储为train.txt和val.txt
image.png

CRF模型

查看原文

赞 0 收藏 0 评论 0

略多 发布了文章 · 2020-11-10

neo4j 及 它所需要的jdk安装日志

我在安装环境的时候往往都是自己从官网安装,这样可以保证我通过最直接的途径学到更靠谱的方法。

  • 环境:ubuntu16台式机
  • 需要避免的坑:

①最好安装neo4j,因为neo4j(v3)安装后在bin中使用命令$sudo ./neo4j console时候会出error
②要记住,(v4)对应jdk11,(v3)对应jdk8

一、jdk11安装

1、下载
进入oracle(甲骨文)官网,找到自己对应的jdk版本;
注意:之前下错了版本:Linux ARM 64 Compressed Archive
应该是这个Linux x64 Compressed Archive,下载到本地;
注意现在需要登录后才能下载,你需要先注册一个账号!

2、安装&配置
①自己的下载目录,把这个文件复制到自己的java安装目录下,一般在/usr/local下,我们mkdir一个java目录。
(即jdk-11.0.9_linux-x64_bin.tar.gz复制到/usr/local/java
②使用tar -zxvf命令解压缩,删除这个压缩包,此时当前目录下有了一个目录jdk-11.0.9,名字不要紧,也许你看到这篇文章的时候版本都更新到20了。
③配置java环境:
$ sudo vim /etc/profile,添加两条:

export JAVA_HOME=/usr/local/java/jdk-11.0.9
export PATH=${JAVA_HOME}/bin:${PATH}

上面那个JAVA_HOME后面的位置就是你的目录绝对地址
④使环境配置生效:
source etc/profile
3、坑
①jdk11没有jre这个目录,是需要自己根据需要来安装的,但我们只到这一步就够了,环境变量不需要jre目录,故别多此一举。
②关于每次都需要source才能检验java -version命令
这篇博客彻底解决问题拿走不谢。
还是粘出来吧:

sudo update-alternatives --install /usr/bin/javac _javac_ /usr/local/java/jdk-11.0.9/bin/javac 300
sudo update-alternatives --install /usr/bin/java _java_ /usr/local/java/jdk-11.0.9/bin/java 300

③想要彻底删除jdk的时候(完美卸载JDK):

二、neo4j安装

上官网:
找的是社区服务器版:
Neo4j 4.1.4 (tar)
①下载到自己的下载目录;
②复制到/usr/local/neo里边;
③用tar -zxvf ,命令解压
④进conf目录修改配置文件neo4j.conf:
打开这几个注释:

dbms.connectors.default_listen_address=0.0.0.0      #允许远程访问
dbms.connector.bolt.enabled=true            #开启bolt服务,默认端口7687
dbms.connector.http.enabled=true            #开启http服务,默认端口7474
dbms.connector.https.enabled=true           #开启https服务,默认端口7473

然后就可以了,参考《ubuntu服务器上安装neo4j》


现在都已经安装完成,直接sudo bin/neo4j console或者start即可。

查看原文

赞 0 收藏 0 评论 0

略多 发布了文章 · 2020-11-09

知识图谱描边1.1——从NER上手

本文概述:复现知乎-KG开源项目集中的BERT-NER-pytorch项目之后,进行的一些学习记录,对同样刚入行的小白来说有参考意义。

资料:关于BERT模型中的transformer介绍,必须分享的是Jay Alammar的动画图,看完后我捶胸顿首的为什么没有早早看到这样的国外佳作?


一、准备工作:

1、数据集

数据集的组织方式、处理方式都是深度领域的重头戏,可以说一个算法工程师80%的时间都在和数据打交道。现在我们来介绍一下数据集:

  • 获取方式:直接用google扩展gitzip从git直接下载的,

生数据只有3项,是
image.png
其中train集里有45000个句子,test集是3442,我们需要人为划分出val集。

  • 生数据形式-类似下面这个样子,我们来看一下msra_train_bio的前17行(一共176042行):
中    B-ORG
共    I-ORG
中    I-ORG
央    I-ORG
致    O
中    B-ORG
国    I-ORG
致    I-ORG
公    I-ORG
党    I-ORG
十    I-ORG
一    I-ORG
大    I-ORG
的    O
贺    O
词    O
各    O
  • tags(只有三种实体:机构,人,位置):
O
B-ORG
I-PER
B-PER
I-LOC
I-ORG
B-LOC

ps:可以看到,采用的是BIO标注法,我们当然可以修改!

  • 待会儿要划分数据集为(3个目录):
DatasetNumber
training set42000
validation set3000
test set3442

得到三个目录?。

  • 数据处理后的形式(各取前两条):

sentences.txt文件:

如 何 解 决 足 球 界 长 期 存 在 的 诸 多 矛 盾 , 重 振 昔 日 津 门 足 球 的 雄 风 , 成 为 天 津 足 坛 上 下 内 外 到 处 议 论 的 话 题 。
该 县 一 手 抓 农 业 技 术 推 广 , 一 手 抓 农 民 科 技 教 育 和 农 技 水 平 的 提 高 。
而 创 新 的 关 键 就 是 知 识 和 信 息 的 生 产 、 传 播 、 使 用 。

相应的,tags.txt文件:

O O O O O O O O O O O O O O O O O O O O O B-LOC I-LOC O O O O O O O O B-LOC I-LOC O O O O O O O O O O O O O O
O O O O O O O O O O O O O O O O O O O O O O O O O O O O O O
O O O O O O O O O O O O O O O O O O O O O O O

2、准备模型和训练好的模型参数

在实验中尝试过多次之后,发现模型参数获取其实也不难,
作者复现代码的时候,没有直接可获取的pt下的模型参数,现在就是一个参数的事。
train.py代码中,创建model时候有这么一句:

model = BertForTokenClassification.from_pretrained()

我们点进去查看from_pretrained()方法,在pytorch_pretrained_bert目录下的modeling.py文件中。
可以通过路径名或者url来获取模型和参数(后来训练中有一点小插曲,我就舍弃了作者提供的,自己从里面下载了压缩包,后面会提到):

PRETRAINED_MODEL_ARCHIVE_MAP = {
    'bert-base-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased.tar.gz",
    'bert-large-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased.tar.gz",
    'bert-base-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased.tar.gz",
    'bert-large-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased.tar.gz",
    'bert-base-multilingual-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-uncased.tar.gz",
    'bert-base-multilingual-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-cased.tar.gz",
    'bert-base-chinese': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese.tar.gz",
}

ps:先获取tf的模型参数再转换为pt,费了我好大劲,直接从Transformer库下载了一个convert_tf_checkpoint_to_pytorch.py文件到tf模型参数目录下,操作了一番得到了pt的.bin文件,好在现在一句话的事。

3、train.py文件解读

数据集的处理和超参数的设置简单,不涉及到我们文章的核心,这里就不做过多记录,想了解则去看github项目,传送门
这个文件放的东西比较多,有
①parse,params设置,logger日志
②dataloader,model,optimizer和train_and_evaluate

  • 参数parse(这个参数解析是运行.py文件时候的参数)
parser = argparse.ArgumentParser()
parser.add_argument('--data_dir', default='NER-BERT-pytorch-data-msra', help="Directory containing the dataset")
parser.add_argument('--bert_model_dir', default='pt_things', help="Directory containing the BERT model in PyTorch")
……

之后在main中,记录参数到内存:
args = parser.parse_args()
在接下来的使用中通过args.param来获取参数,

  • logger日志

相关的处理放在了utils.py中,在train.py中直接:

#创建
utils.set_logger(os.path.join(args.model_dir, 'train.log'))
# 需要记录的时候:
logging.info("device: {}, n_gpu: {}, 16-bits training: {}".format(params.device, params.n_gpu, args.fp16))
  • model

简单,两句话:

model = BertForTokenClassification.from_pretrained(args.bert_model_dir, num_labels=len(params.tag2idx))
model.to(params.device)

里边包括模型的加载和参数的加载,在训练时我们在看到modelconfig之后,还会看到两句提示:

Weights of BertForTokenClassification not initialized from pretrained model: ['classifier.weight', 'classifier.bias']
Weights from pretrained model not used in BertForTokenClassification: ['cls.predictions.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight', 'cls.seq_relationship.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.LayerNorm.bias']

(开始我还以为这是模型参数导入失败,就去查找解决办法。

我还一度以为pytorch_model.bin文件中的参数和模型不匹配,重新从源码提供的链接下载了压缩包,但还有这两句话。

结果在Google上一个论坛发现这两句话是成功调用参数的意思。)
解谜:这个model里边包括了embedding和bert NER两个部分,其中模型的参数应该是embedding部分的,而具体的NER任务应该使用我们自己的数据来训练!
读model源码:各层详细解释
查看model(BertForTokenClassification):
有三层:
①BertModel
BertEmbeddings:里边含有多层

    (position_embeddings): Embedding(512, 768)
    (token_type_embeddings): Embedding(2, 768)
    (LayerNorm): BertLayerNorm()
    (dropout): Dropout(p=0.1, inplace=False)
BertEncoder:里边有12层encoder,每一个encoder都是一个BertLayer:
    (attention): BertAttention  #重中之重,有机会一定要刷
    (intermediate): BertIntermediate
    (output): BertOutput
BertPooler

②DropOut
③Linear

  • optimizer

full_finetuning

4、单机多卡并行和fp16

  • 多卡

要把模型和数据都分配到多卡;
①指定虚拟gpu:
os.environ["CUDA_VISIBLE_DEVICES"] = '1,2,3,0'
代表虚拟地址对应的物理地址为1,2,3,0(当时因为师兄的主gpu是物理0号卡,所以避开这个剩余显存相对较小的卡)
image.png
ps:截图时师兄已经不再用了。

②在准备模型和数据之前,放这一句:
params.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
估计'cuda'后面的部分是作者不想看到报错……
注意:如果要写'cuda:[num]',请写前面指定gpu时候的第一个,在这里就是1!

③随机种子分配到各个gpu:

# 设置可重复试验的随机种子
random.seed(args.seed)
torch.manual_seed(args.seed)    #给cpu设置
if params.n_gpu > 0:
    torch.cuda.manual_seed_all(args.seed)  # set random seed for all GPUs

ps:如果单卡,只是去掉all;

④给model分配多卡:

model = BertForTokenClassification.from_pretrained(args.bert_model_dir, num_labels=len(params.tag2idx))
model.to(params.device)
if params.n_gpu > 1 and args.multi_gpu:
    model = torch.nn.DataParallel(model)

⑤最后,给数据分配多卡:

# Initialize the DataLoader
data_loader = DataLoader(args.data_dir, args.bert_model_dir, params, token_pad_idx=0)
# Load training data and test data
train_data = data_loader.load_data('train')
val_data = data_loader.load_data('val')
……
在train()函数之前:
# data iterator for training
train_data_iterator = data_loader.data_iterator(train_data, shuffle=True)
# Train for one epoch on training set
train(model, train_data_iterator, optimizer, scheduler, params)

在data_iterator()中设置的给每个batch分配的卡:

# shift tensors to GPU if available
batch_data, batch_tags = batch_data.to(self.device), batch_tags.to(self.device)
yield batch_data, batch_tags

这里的self.device是类接受的参数。

  • fp16

参考:一篇讲fp16加速原理的CSDN
fp16使用2字节编码存储
优点:内存占用少(主)+ 加速计算
缺点:加法操作容易上下溢出
(有机会可以专门实验一下)

5、进度条工具

此处计算出每一个epoch下计算1400个batch,
所以把进度条放到每一个epoch中:

t = trange(params.train_steps)
for i in t:
    # fetch the next training batch
 batch_data, batch_tags = next(data_iterator)
 ……
 loss = model(~)
 ……
 t.set_postfix(loss='{:05.3f}'.format(loss_avg()))

结果展示:
image.png

6、评价指标库metrics

可以使用的结果评价指标(冰山一角):

from metrics import f1_score
from metrics import accuracy_score
from metrics import classification_report

模板:

metrics = {}
f1 = f1_score(true_tags, pred_tags)
accuracy=accuracy_score(true_tags, pred_tags)
metrics['loss'] = loss_avg()
metrics['f1'] = f1
metrics['accuracy']=accuracy
metrics_str = "; ".join("{}: {:05.2f}".format(k, v) for k, v in metrics.items())
logging.info("- {} metrics: ".format(mark) + metrics_str)

结果展示:
image.png

如何区分精确率和召回率?
image.png

7、多次实验存在的问题

我的f1分数一直都在50以下,但是精确率一直都在97%附近,最近因为要肝链接抽取,所以这部分先放一下把。回头补全

查看原文

赞 0 收藏 0 评论 0

略多 发布了文章 · 2020-10-16

知识图谱描边1.0——从Embedding到BERT

此文是本人对一篇文章(张俊林——知乎——从Word Embedding到Bert模型—自然语言处理中的预训练技术发展史)的整理笔记。文章重要原因如下:

  • 作者对Embedding研究很透彻。
  • 文章对NLP预处理的历史脉络做了很好的梳理。

文章结论:BERT模型效果爆炸,但创新一般(真自信)。
下面按照历史沿革分为 个部分做记录:
一、预训练——从CV讲到NLP
二、WE考古史
三、从Word Embedding到ELMo
四、从Word Embedding到GPT
五、BERT诞生

一、预训练——从CV讲到NLP

底层神经元学到的是线段等特征,跟具体任务无关;
任何任务都喜欢用ImageNet作预训练:
通用性好——量大、类多、领域无关

那么NLP领域有预训练技术吗?
——早期的预训练技术:Word Embedding!


二、WE考古史

思维:语言模型 -- NNLM -- 副产品WE -- 两个方法 --下游任务使用 -- frozen和fine-turning -- 缺点

语言模型讲起,这张图在《数学之美》里见过。
image.png
NNLM(神经网络语言模型),它生于2003年,火于2013年:
image.png
图中,每一个单词乘以矩阵Q,得到各自的C(Wi),再去计算……
其实副产品Q就是WE的映射表

2013年最火的语言模型WE工具:①Word2Vec 和 ②Glove
word2ve两种训练方法
①CBOW(预测句子中被抠掉的词),记住CBOW。
②skip-gram(一个词预测上下文)

效果:研究者发现一个单词表达成为word Embedding后,很容易找到语义相近的其他词汇!
image.png

但,WE对下游任务的提升只有1~2个点,那么缺点在哪里?
多义词 bank

三、从Word Embedding到ELMo

动态模型 ELMO(Embedding from Language Models)
原文:Deep contextualized word representation
image.png
三层WE,越往上语义越丰富。
ps:如果此图看不懂,可从seq2seq开始了解,传送门《Seq2Seq 模型详解》
训练好后ELMo如何使用
每个Embedding分配一个可学习权重,按权重累加预训练,称为“Feature-based Pre-Training”!

四、从Word Embedding到GPT

GPT Generative Pre-Training
也采用两阶段过程:语言模型训练+fine-turning解决下游任务
前者:特征提取器由RNN换成了Transformer,单向语言模型(吃亏)
后者:下游任务要向GPT网络看齐
image.png

注意力机制讲解
可视化介绍Transformer
哈佛NLP研究组的

五、BERT诞生

预训练阶段采用了类似ELMO的双向语言模型,语言模型规模更大
如果ELMO采取Transformer作为特征提取器,那么估计Bert的反响不会很火爆;

查看原文

赞 0 收藏 0 评论 0

略多 发布了文章 · 2020-10-11

知识图谱描边——知识图谱的数值表示

  • 核心词汇:embedding
  • 科研方向的核心问题:如何为KG中的实体与关系求得最优的向量化表示

一、概述

1、知识图谱有几种表示方法?

KG有两种表示方法:
其一,基于图的表示(方便人理解);
其二,数值表示(将知识图谱中的数据转化为机器可以“理解”的内容);

2、embedding是干什么的?

让深度学习模型能够使用KG中的知识。
全面的分为:翻译(TransE, TransH, TransR, etc)、双线性(RESCAL, DisMult, ComplEx, etc)、双曲几何(Poincare, MuRE, etc)、神经网络(ConvE, CapsE, etc)、旋转(RotatE, QuatE, DihEdral, etc)类别

一篇不错的文章参考知乎:embedding的作用

3、有哪些主流模型?

Trans系列的。
其实数值里边有基于距离的模型与基于翻译的模型。
按时间先后顺序为:
基于距离的模型:
SE,这篇文章《Learning Structured Embeddings of Knowledge Bases》2011
基于翻译的模型:把关系看成翻译
Trans E
Trans H
Trans R
Trans D
新的在不断改正着旧的。

二、发文心路历程:

——想做推理方向
——看ACL2020一篇文章
《Improving Multi-hop Question Answering over Knowledge Graphs using Knowledge Base Embeddings》
——注意到embedding和Trans E模型
——看《知识图谱——概念与技术》 2.2知识表示
去找系统性的讲解
——系统的了解了一下embedding

查看原文

赞 0 收藏 0 评论 0

略多 发布了文章 · 2020-09-21

scrapy(三) 爬取ConceptNet

一、工作背景

放弃之前的计划
经过开会讨论,仅爬取心理学领域的知识来构建心理沙盘的知识图谱是不可取的(事实上,项目的目标是根据用户设计的沙盘场景推理出用户的心理状态,而不是做心理学百科知识的科普)。
这一知识图谱构建方向上的改变归功于我们小组的讨论和 《知识图谱-概念与技术》 这本书对我的启发,一些知识要点如下(可以跳过直接查看项目细节):

1、知识图谱广义概念

为了讲明白我对项目的理解,还要从人工智能说起(想在这里一次性的的捋清楚,也许将来会加入一个链接):
image
图:知识图谱的学科地位

人工智能有3个学派:符号主义连接主义行为主义
知识工程源于符号主义,为了有效的应用知识,首先要在计算机系统中合理的表示知识;
知识表示的一个重要方式就是知识图谱
注意:知识图谱只是知识表示中的一种,除了语义网络以外,谓词逻辑、产生式规则、本体、框架、决策树、贝叶斯网络、马尔可夫逻辑网都是知识表示的形式;

2、KG研究意义

KG是认知智能的基石

  • 机器理解数据的本质是从数据到知识图谱中的知识要素(实体概念、关系)的映射
  • 理解过程可以视作建立从数据(文本、图片、语音、视频)到KG(脑海)中实体、概念、属性之间的映射过程;

几点KG对于认知智能的重要性:
①机器语言认知
人类对语言的理解建立在认知能力基础之上,所以解释了我们听不懂西方的笑话故事。
语言理解需要背景知识,机器理解自然语言当然也需要背景知识了。
②赋能可解释人工智能
“解释”与符号化知识图谱密切相关,人只能理解符号而无法理解数值,书中有三个很好的例子:

问鲨鱼为什么可怕?你可能解释因为鲨鱼是肉食动物——用概念在解释;
问鸟为什么能飞翔?你可能会解释因为它有翅膀——用属性解释;
问前段时间鹿晗和关晓彤为什么会刷屏?你可能会解释因为她是他女朋友——用关系在解释;

③有助于增强机器学习的能力
人类的学习高效、健壮,并不需要机器学习那样庞大的样本量,根本原因在于人类很少从零开始学习,人类擅长结合丰富的先验知识。

3、常识知识图谱

知识图谱可以根据其所涵盖的知识分为四类:事实知识、概念知识、词汇知识和常识知识
有了1和2的铺垫,我们选择为心理沙盘项目建立一个常识知识图谱的理由就明确了,每一个沙盘实体都来自现实世界,我们要做的是高度还原人脑中实体与实体、实体与概念、概念与概念这些映射关系,从而能够让心理测试结果更强健和有说服力
以沙盘描述中的虾为例:

①虾看似慢悠悠的游,遇到对手时它勇猛奔上去伸展双臂与它博斗,
打断双臂在所不惜,象征充满阳刚之气。
②虾须流畅,飘逸,虾尾随移其形而动,象征个性和目标。

我们可以构建这样一种联系:
image.png

二、工作思路

沙盘心理分析师提供了一个entity文件,里边有大约600个实体,长这样:
image.png
1、以这600个实体为根节点,从ConceptNet上爬取实体的所有关系,再递归关系所牵的另一边实体,提取的内容有:
entity1、relation、entity2、att1、att2、weight、id
此七项内容
2、根据这些内容先构建一个知识图谱

三、scrapy实操

1、在之前创建的爬虫工程中,新建一个spider文件,还是通过命令来实现(工程项目创建的具体操作步骤参考scrapy(一) 爬取心理学领域词汇

scrapy genspider -t basic conceptSpider api.conceptnet.io

2、分析页面,试探爬取——循环往复直到成型

①方法论:
当有一个新的爬虫任务时,我必然不能也绝不可能做到直接写成spider文件,解注释settings.py中的pipline,写items.py文件,写pipline.py文件。因为网页是否允许爬取、网页的格式、我们想要的内容在哪、是否需要递归等,都需要去debug,慢慢的磨出来。也就是先别解注释settings.py中的pipline,只消使用命令
$ scrapy crawl conceptSpider --nolog
一步步的迭代检验我们的规则是否写对了。
②页面分析:
查看ConceptNet官方文档,得知这种api类型的网页中,每一个key代表什么。
先给start_urls中只添加了一个‘http://api.conceptnet.io/c/en/apple’
来专注于分析这个页面,显然每一页都有共性,页面部分截图如下:
image.png
其中’edges‘是一个list形式,每一项是一个关系,连接着与apple相关的实体;
在网页底部,有一个键值是‘view’,是管前后页的,也就是说里边有下一页的url;
image.png
网页解析:

# 将json格式的api页面解析为用字符串表示的字典
js_str=response.xpath("//pre").xpath("string(.)").extract()[0].strip()
# 将字符串转化为字典
js=json.loads(js_str)

规则化提取:

for edge in js['edges']:
    str=edge['@id'].replace('[','').replace(']','').replace('/a/','').split(',')
    # 只取英文关系
    if str[1][3:6]!='en/' or str[2][3:6]!='en/':
        continue
    # 取两个实体
    e1=str[1].replace('/c/en/','').replace('/n','').replace('/wn','')[:-1].split('/',1)
    e2=str[2].replace('/c/en/','').replace('/n','').replace('/wn','')[:-1].split('/',1)
    # 取关系
    r=str[0].replace('/r/','')[:-1]
    # 摘除后边的标识,放到属性里
    e1_att=''
    e2_att=''
    if len(e1)==2:
        e1_att=e1[1]
    if len(e2)==2:
        e2_att=e2[1]

注:测试规则化提取正常后,就可以在上边的for循环里边用item传给pipline去输出了。
③页面跳转

if 'view' in js and 'nextPage' in js['view']:
    relPos = js['view']['nextPage']
    print("进入衍生页面执行parse:")
    nexthref = self.url_std+relPos
    print(nexthref)
    yield scrapy.Request(nexthref, callback=self.parse)

注意:这里有坑,如果只判断'nextPage' in js['view'],你就会发现有的实体页面中没有view这个键值,会产生错误,但不影响pipline输出的结果。

*3、解决网页访问频率限制问题

发现问题:
在只有两个url的start_urls列表上测试没有问题,但是将400多个url放入start_urls中后,得到的结果每次都只有几百,几千条,最多1万条。
细节: 观察输出文件triplets.txt和终端显示,发现每次在第602个页面,第1206个页面出现卡壳,最终可能停止输出。
曲折的解决过程:
以为是输出的时候内存爆了导致write文件终止或者是txt文件大小有限制导致提前关闭文件。结果换成csv格式也不好使,都要考虑使用数据库了;

去掉--nolog后执行$ scrapy crawl conceptSpider,发现问题在于爬取阶段而不是pipline的数据写出阶段。日志中有一些url反馈结果是 [429] ,也就是页面被访问频繁导致的拒绝访问。

解决办法的帖子 具体如下:
可使用 429 状态码,同时包含一个 Retry-After 响应头用于告诉客户端多长时间后可以再次请求服务。

middlewares.py:# 当状态码是429的时候 爬虫暂停60秒再爬取

import time
from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message

class TooManyRequestsRetryMiddleware(RetryMiddleware):
    def __init__(self, crawler):
        super(TooManyRequestsRetryMiddleware, self).__init__(crawler.settings)
        self.crawler = crawler
    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)
    def process_response(self, request, response, spider):
        if request.meta.get('dont_retry', False):
            return response
        elif response.status == 429:
            self.crawler.engine.pause()
            print("速度太快  暂停60秒")
            time.sleep(60)  # If the rate limit is renewed in a minute, put 60 seconds, and so on.
            self.crawler.engine.unpause()
            reason = response_status_message(response.status)
            return self._retry(request, reason, spider) or response
        elif response.status in self.retry_http_codes:
            reason = response_status_message(response.status)
            return self._retry(request, reason, spider) or response
        return response

settings.py:

DOWNLOADER_MIDDLEWARES = {  # 开启暂停中间件
   'psySpider.middlewares.PsyspiderDownloaderMiddleware': 543,
}

RETRY_HTTP_CODES = [429, 500, 403]  # 这个状态重试

DOWNLOAD_DELAY = 0.5
RANDOMIZE_DOWNLOAD_DELAY = True # 发完一个请求 随机暂停
4、piplines.py写法
# 直接照着爬虫名字补上就可以
class ConceptspiderPipline(object):
    def __init__(self):
        self.file = open("./triplets1.csv","w",encoding='utf-8')
        self.writer = csv.writer(self.file, dialect="excel")

    def process_item(self, item, spider):
        theme=item['theme']
        entity_1 = item['entity_1']
        entity_2 = item['entity_2']
        relation = item['relation']
        att1=item['e1_att']
        att2=item['e2_att']
        weight=item['weight']
        id=item['id']
        self.writer.writerow([entity_1,entity_2,relation,att1,att2,weight,id])
        return item
5、items.py
# 直接照着爬虫名字补上就可以
class ConceptspiderItem(scrapy.Item):
    theme=scrapy.Field()
    entity_1=scrapy.Field()
    entity_2=scrapy.Field()
    relation=scrapy.Field()
    e1_att=scrapy.Field()
    e2_att=scrapy.Field()
    weight=scrapy.Field()
    id=scrapy.Field()
查看原文

赞 0 收藏 0 评论 0

略多 发布了文章 · 2020-09-18

*scrapy(二) 递归爬取心理学词汇

一、工作背景

1、简单回顾

上一期知识获取思路:从搜狗下载心理学词库,然后爬取这些词汇的百度百科页面。但是这些词汇只有3600个,现在我们要丰富实体,为下一阶段的命名实体识别做准备。

2、下一步工作计划

step1:爬取百度百科心理学分类下的所有名词;
step2:所有整合后的词汇,人为对其进行实体分类;
step3:爬取《壹心理》中的文章,分词、标注、命名实体识别(为关系抽取做准备)
ps:其实这个计划由于某些原因被放弃了(因为这篇文章是草稿,这段话是隔了两周后添加的)

3、这篇文章主要内容:

递归爬取下图(百度百科开放分类页面)中所有的标签及子类别下的所有标签(相关分类栏目除外):
image
目标: 上图中①②③中的条目是我们需要添加到词汇集合中的,②下的条目还需要进一步到下级链接去爬取。
注意: ③下的条目不够完备,需要去下一页爬取,下面会详细讲解步骤。

二、工作记录

1、此爬虫有一个根节点,所以start_urls内只有一个url:
start_urls.append('http://baike.baidu.com/fenlei/心理学')
2、想要迭代爬取一个标签下的所有标签:
main_sub=response.xpath("./div[@class='category-title ']/*") # 使用了星号,再:

for sub in main_sub:
    nexthref=sub.xpath("./@href")
    if(nexthref!=[]):
        nexthref=nexthref.extract()[0].strip()
        nexthref = response.urljoin(nexthref)
        yield response.follow(nexthref, callback=self.parse)
        print(nexthref)
    title=sub.xpath("string(.)").extract()[0].strip()
    if(title!="|" and title!="下级分类"):
        item['name']=title
        yield item

即可;
3、如上代码中,不论在程序任何位置,只要得到url,即可对其执行parse与yield:

细节

1、关于下一节内容如何爬取,附上解决问题的链接《scrapy中如何获取下一页链接》
注意:不能手动往start_urls中append()添加新的链接,因为在解析时候已经把队列存入内存。
2、两种在parse内继续爬取链接的方式

yield response.follow(nexthref, callback=self.parse)
yield scrapy.Request(nexthref, callback=self.parse)

3、解决一个bug
递归爬取链接无效:解决:1、第一次不能是因为allowed_domains的限制2、第二次是因为在div标签下有两个链接:<上一页>和<下一页>,果然结果只爬取了3个,第2页,第1页和第1页(因为scrapy的机制,爬过第二页所以不会重新爬取了)
4、对于yield item理解
其实很简单,在哪用都成,就是一个生成器

查看原文

赞 0 收藏 0 评论 0

略多 发布了文章 · 2020-08-31

关系抽取综述《More Data, More Relations, More……》

Section2 介绍一些已有的RE方法
Section3 前沿RE正在解决的4个问题

Section2

1、关系抽取(RE)的功能
完整的关系抽取系统包括三部分:
1)NER 2)实体链接 3)关系分类
关系分类是重点和难点。
image
如图:关系分类就是把包含两个实体的句子分类为已经定义好的某种关系。
2、介绍3类方法

  • 模式方法
  • 统计方法

①基于特征的方法(为实体对及其上下文设计句法、语法、语义特征用于分类)。
②图方法——以有向无环图的方式抽取实体,文本和关系间的依赖,并使用推理模型来识别正确的关系。
③编码文本到低维的语义空间,再从这个语义向量中提取关系(受NLP其他任务的嵌入模型影响)。
④此外还有使用KG嵌入。
局限:人工设计特征或核函数,或图模型的能力有限。

  • 神经网络

优势: 更高效的抽取语义特征,泛化到更大范围的数据。
-Recursive NNs:递归学习句子的构成表示;
-CNNs:捕捉句子局部特征;
-RNNs:更好的处理长序列语句;
-GNNs:构造词汇/实体图用于推理;
-Attention-based NNs:聚合全局的关系特征;
不同于SRE ,NRE主要使用词汇编码位置编码而不是手工提取特征。
注释:位置编码指定词汇和实体间的相对距离。
-使用CNNs和RNNs来编码最短路径依赖。

  • 自2013年以来,神经网络方法带来了极大的能力提升(图为SemEval-2010数据集上的逐年分数)

image

Section3

Section1中已经做过介绍:

作者将现阶段前沿工作分为4个方向:
使用更多数据(我理解为能放心使用大量数据,因为质量放心)
大量人工标注数据的工作可以被远程监督来替代,可远程监督下产生的自动标注数据会产生噪声样本。另外,远程监督仅使用简单的包含实体对的句子,严重削弱了提取能力。
所以这个方向的主要任务是设计一种模式来得到高质量和高覆盖率的数据。
达到更有效的学习
人类能够从一件事中学习到普遍性,所以模型也要学会去更有效地利用少得可怜的数据。
处理更复杂的内容
所有RE模型都在提取句内关系,而不是句间关系
开放域
如何自动发现没有事先定义好的关系。

1、使用更多数据

远程监督用来自动label,具体做法是用KGs中的关系来label含有同样实体对的文本。
缺点: 引入错误标签。
解决方法:
image
因为远程监督这一块最近没有需求(我们的标记数据充足),之后再做笔记。

2、更高效学习

针对长尾分布(Addition中介绍什么是长尾分布)
解决方法:
小样本学习:
其场景通常设为:N-way K-shot,即N种随机取样的关系,每个关系具有K个样本。
两种手段:
image.png
应用级别的挑战:
① 领域迁移;
② 无法检测不属于任何给定的N个关系。
③ 现有评估协议高估模型表现,详细:
Few-shot关系抽取随机采样N个关系。通常,关系N很少(5或10);另一方面,由于很大概率采样的是N个不同关系,这就简化成了简单的分类任务。本文用两个实验来可视化问题(如下图):(A)关系数量增大,性能急剧下降;(B)手动选择5个语义相似的关系而非随机采样,结果急剧下降。这表明现有的少量模型可能会掩盖关系之间的简单文本提示,而不是真正地理解上下文的语义。
image.png

3、处理更复杂的上下文

大多数关系事实蕴含在诸如文档之类的复杂语境中,而不是从单个句子中提取,目前已有的方案如下:

  • 基于语法的方法;
  • 建立句子间实体图,利用实体间的multi-hop路径来推断正确的关系;
  • 利用图结构神经网络对句子间的相关性进行建模以进行关系抽取,从而引入记忆和推理功能;

一些值得更进一步探索的方向:

  • 需要阅读、记忆和推理才能发现句子中的关系事实,大多数RE模型在这些能力上很弱;
  • 跨文档
  • 利用搜索引擎自动搜索和分析关系抽取任务的背景信息。
4、面向开放领域

当前未在项目考虑之列,因为这一部分涉及下一代科学革命的知识。

Addition:

文中一些terms
长尾关系:那些通常使用的关系才包含足够多的训练实例,可大部分的关系的实例并不充足。
image
上图代表两个数据库下的长尾分布,可以看到很多关系甚至不足10个实例。

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 0 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2020-08-27
个人主页被 768 人浏览