2021-07-08

transformer 资源总结
transformer中的positional encoding(位置编码)
https://blog.csdn.net/Flying_sfeng/article/details/100996524
代码实现部分参考:
https://github.com/DA-southampton/NLP_ability/blob/master/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/%E6%96%87%E6%9C%AC%E5%8C%B9%E9%85%8D_%E6%96%87%E6%9C%AC%E7%9B%B8%E4%BC%BC%E5%BA%A6/src/models.py

https://github.com/DA-southampton/NLP_ability/blob/master/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/%E6%96%87%E6%9C%AC%E5%8C%B9%E9%85%8D_%E6%96%87%E6%9C%AC%E7%9B%B8%E4%BC%BC%E5%BA%A6/src/models.py
y关键词提取资源总结

  • List item

NLP关键词提取方法总结及实现 https://blog.csdn.net/asialee_bird/article/details/96454544

## 正向最大匹配算法
def cut_words(split_sentence,words_dic):
    #统计词典中最长的词
    max_length = max(len(word) for word in words_dic)
    sentence = split_sentence.strip() ## 简单清理一下
    #统计序列长度
    words_length = len(sentence) ## 在第二个循环的时候,我需要不停的和字典最大长度比较,取最小值作为基线
    #存储切分好的词语
    cut_word_list = []
    while words_length > 0: ## 第二个循环,找到一个之后,循环的去找下一个符合要求的
        max_cut_length = min(max_length, words_length)
        subSentence = sentence[0 : max_cut_length]
        while max_cut_length > 0: ## 第一个循环,迭代找到符号字典的
            if subSentence in words_dic:
                cut_word_list.append(subSentence)
                break
            elif max_cut_length == 1:
                cut_word_list.append(subSentence)
                break
            else:
                max_cut_length = max_cut_length -1
                subSentence = subSentence[0:max_cut_length]
        sentence = sentence[max_cut_length:]
        words_length = words_length - max_cut_length
    return cut_word_list
input_str="今天天气真不错啊,适合出去旅游"
bmm_word_list = cut_words(input_str, words_dic)
print(bmm_word_list)
##逆向最大匹配
def cut_words(raw_sentence,words_dic):
    #统计词典中词的最长长度
    max_length = max(len(word) for word in words_dic)
    sentence = raw_sentence.strip()
    #统计序列长度
    words_length = len(sentence)
    #存储切分出来的词语
    cut_word_list = []
    #判断是否需要继续切词
    while words_length > 0:
        max_cut_length = min(max_length, words_length)
        subSentence = sentence[-max_cut_length:]
        while max_cut_length > 0:
            if subSentence in words_dic:
                cut_word_list.append(subSentence)
                break
            elif max_cut_length == 1:
                cut_word_list.append(subSentence)
                break
            else:
                max_cut_length = max_cut_length -1
                subSentence = subSentence[-max_cut_length:]
        sentence = sentence[0:-max_cut_length]
        words_length = words_length -max_cut_length
    cut_word_list.reverse()
    return  cut_word_list
总结

参考链接:常见文本相似度计算方法简介 - 李鹏宇的文章 - 知乎
https://zhuanlan.zhihu.com/p/88938220

短文本相似度算法研究 - 刘聪NLP的文章 - 知乎
https://zhuanlan.zhihu.com/p/111414376

现在工业界有哪些比较实用的计算短文本相似度的算法或者模型? - vincent的回答 - 知乎
https://www.zhihu.com/question/342548427/answer/806986596

推荐系统中的深度匹配模型 - 辛俊波的文章 - 知乎
https://zhuanlan.zhihu.com/p/101136699

四种计算文本相似度的方法对比 - 论智的文章 - 知乎
https://zhuanlan.zhihu.com/p/37104535

深度文本匹配发展总结 - xiayto的文章 - 知乎
https://zhuanlan.zhihu.com/p/40741576

常见文本相似度计算方法简介 - 李鹏宇的文章 - 知乎
https://zhuanlan.zhihu.com/p/88938220

参考链接:
https://zhuanlan.zhihu.com/p/48188731

import json
import os
from py2neo import Graph, Node, Relationship

class GoodsKg:
def init(self):
cur = ‘/’.join(os.path.abspath(file).split(’/’)[:-1])
self.data_path = os.path.join(cur, ‘data/goods_info.json’)
self.g = Graph(
host=“127.0.0.1”, # neo4j 搭载服务器的ip地址,ifconfig可获取到
http_port=7474, # neo4j 服务器监听的端口号
user=“lhy”, # 数据库user name,如果没有更改过,应该是neo4j
password=“lhy123”)
return

'''读取数据'''
def read_data(self):
    rels_goods = []
    rels_brand = []
    goods_attrdict = {}
    concept_goods = set()
    concept_brand = set()
    count = 0
    for line in open(self.data_path):
        count += 1
        print(count)
        line = line.strip()
        data = json.loads(line)
        first_class = data['fisrt_class'].replace("'",'')
        second_class = data['second_class'].replace("'",'')
        third_class = data['third_class'].replace("'",'')
        attr = data['attrs']
        concept_goods.add(first_class)
        concept_goods.add(second_class)
        concept_goods.add(third_class)
        rels_goods.append('@'.join([second_class, 'is_a', '属于', first_class]))
        rels_goods.append('@'.join([third_class, 'is_a', '属于', second_class]))

        if attr and '品牌' in attr:
            brands = attr['品牌'].split(';')
            for brand in brands:
                brand = brand.replace("'",'')
                concept_brand.add(brand)
                rels_brand.append('@'.join([brand, 'sales', '销售', third_class]))

        goods_attrdict[third_class] = {name:value for name,value in attr.items() if name != '品牌'}

    return concept_brand, concept_goods, rels_goods, rels_brand

'''构建图谱'''
def create_graph(self):
    concept_brand, concept_goods, rels_goods, rels_brand = self.read_data()
    # print('creating nodes....')
    # self.create_node('Product', concept_goods)
    # self.create_node('Brand', concept_brand)
    # print('creating edges....')
    # self.create_edges(rels_goods, 'Product', 'Product')
    self.create_edges(rels_brand, 'Brand', 'Product')
    return

'''批量建立节点'''
def create_node(self, label, nodes):
    pairs = []
    bulk_size = 1000
    batch = 0
    bulk = 0
    batch_all = len(nodes)//bulk_size
    print(batch_all)
    for node_name in nodes:
        sql = """CREATE(:%s {name:'%s'})""" % (label, node_name)
        pairs.append(sql)
        bulk += 1
        if bulk % bulk_size == 0 or bulk == batch_all+1:
            sqls = '\n'.join(pairs)
            self.g.run(sqls)
            batch += 1
            print(batch*bulk_size,'/', len(nodes), 'finished')
            pairs = []
    return


'''构造图谱关系边'''
def create_edges(self, rels, start_type, end_type):
    batch = 0
    count = 0
    for rel in set(rels):
        count += 1
        rel = rel.split('@')
        start_name = rel[0]
        end_name = rel[3]
        rel_type = rel[1]
        rel_name = rel[2]
        sql = 'match (m:%s), (n:%s) where m.name = "%s" and n.name = "%s" create (m)-[:%s{name:"%s"}]->(n)' %(start_type, end_type, start_name, end_name,rel_type,rel_name)
        try:
            self.g.run(sql)
        except Exception as e:
            print(e)
        if count%10 == 0:
            print(count)

    return

if name ==‘main’:
handler = GoodsKg()
handler.create_graph()

#!/usr/bin/env python3

coding: utf-8

File: collect_info.py

Author: lhy<lhy_in_blcu@126.com,https://huangyong.github.io>

Date: 19-3-30

import urllib.request
from urllib.parse import quote_plus
from lxml import etree
import gzip
import chardet
import json
import pymongo

class GoodSchema:
def init(self):
self.conn = pymongo.MongoClient()
return

'''获取搜索页'''
def get_html(self, url):
    headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.5.17 (KHTML, like Gecko) Version/8.0.5 Safari/600.5.17"}
    try:
        req = urllib.request.Request(url, headers=headers)
        data = urllib.request.urlopen(req).read()
        coding = chardet.detect(data)
        html = data.decode(coding['encoding'])
    except:
        req = urllib.request.Request(url, headers=headers)
        data = urllib.request.urlopen(req).read()
        html = data.decode('gbk')


    return html

'''获取详情页'''
def get_detail_html(self, url):
    headers = {
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "accept-encoding": "gzip, deflate, br",
        "accept-language": "en-US,en;q=0.9",
        "cache-control": "max-age=0",
        "referer": "https://www.jd.com/allSort.aspx",
        "upgrade-insecure-requests": 1,
        "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/66.0.3359.181 Chrome/66.0.3359.181 Safari/537.36"
    }
    try:
        req = urllib.request.Request(url, headers=headers)
        data = urllib.request.urlopen(req).read()
        html = gzip.decompress(data)
        coding = chardet.detect(html)
        html = html.decode(coding['encoding'])
    except Exception as e:
        req = urllib.request.Request(url, headers=headers)
        data = urllib.request.urlopen(req).read()
        html = gzip.decompress(data)
        html = html.decode('gbk')
    return html


'''根据主页获取数据'''
def home_list(self):
    url = 'https://www.jd.com/allSort.aspx'
    html = self.get_html(url)
    selector = etree.HTML(html)
    divs = selector.xpath('//div[@class= "category-item m"]')
    for indx, div in enumerate(divs):
        first_name = div.xpath('./div[@class="mt"]/h2/span/text()')[0]
        second_classes = div.xpath('./div[@class="mc"]/div[@class="items"]/dl')
        for dl in second_classes:
            second_name = dl.xpath('./dt/a/text()')[0]
            third_classes = ['https:' + i for i in dl.xpath('./dd/a/@href')]
            third_names = dl.xpath('./dd/a/text()')
            for third_name, url in zip(third_names, third_classes):
                try:
                    attr_dict = self.parser_goods(url)
                    attr_brand = self.collect_brands(url)
                    attr_dict.update(attr_brand)
                    data = {}
                    data['fisrt_class'] = first_name
                    data['second_class'] = second_name
                    data['third_class'] = third_name
                    data['attrs'] = attr_dict
                    self.conn['goodskg']['data'].insert(data)
                    print(indx, len(divs), first_name, second_name, third_name)
                except Exception as e:
                    print(e)
    return

'''解析商品数据'''
def parser_goods(self, url):
    html = self.get_detail_html(url)
    selector = etree.HTML(html)
    title = selector.xpath('//title/text()')
    attr_dict = {}
    other_attrs = ''.join([i for i in html.split('\n') if 'other_exts' in i])
    other_attr = other_attrs.split('other_exts =[')[-1].split('];')[0]
    if other_attr and 'var other_exts ={};' not in other_attr:
        for attr in other_attr.split('},'):
            if '}' not in attr:
                attr = attr + '}'
            data = json.loads(attr)
            key = data['name']
            value = data['value_name']
            attr_dict[key] = value
    attr_divs = selector.xpath('//div[@class="sl-wrap"]')
    for div in attr_divs:
        attr_name = div.xpath('./div[@class="sl-key"]/span/text()')[0].replace(':','')
        attr_value = ';'.join([i.replace('  ','') for i in div.xpath('./div[@class="sl-value"]/div/ul/li/a/text()')])
        attr_dict[attr_name] = attr_value

    return attr_dict

'''解析品牌数据'''
def collect_brands(self, url):
    attr_dict = {}
    brand_url = url + '&sort=sort_rank_asc&trans=1&md=1&my=list_brand'
    html = self.get_html(brand_url)
    if 'html' in html:
        return attr_dict
    data = json.loads(html)
    brands = []

    if 'brands' in data and data['brands'] is not None:
        brands = [i['name'] for i in data['brands']]
    attr_dict['品牌'] = ';'.join(brands)

    return attr_dict

if name == ‘main’:
handler = GoodSchema()
handler.home_list()

关键词的提取,也可以称之为文本标签提取。

比如说,”今天这顿烧烤是真不错啊“,在这句话中,”烧烤“这个词就可以被认为是一个关键词,或者说这个句子的一个标签。
这个标签在一定程度上能够表现出这个句子的含义,比如这个”烧烤“,如果用在文本分类任务中,可以隐含带有”美食“这个类别的信息。
这些标签有些时候也可以用在推荐系统的召回,比如直接按照”烧烤“这个标签做一路召回。
对于关键词的提取一般来说分为抽取式和生成式。其实类比到摘要,其实也是分为抽取式和生成式。
生成式有一个缺点就是有些结果不可控,这其实还挺要命的。

对于抽取式,就是从现有的数据中拿出来词组。最差的结果也就是拿出的单词并不重要,不是我们想要的。

我们的重点是在抽取式提取关键词。
关键词的提取可以分为两个步骤:召回+排序
1.召回

召回就是得到文本中的候选关键词,也就是得到这个句子中有可能是关键词的词汇。
这一步,可以做的方法有很多,比如
我们有积累的关键词词库,在这里直接匹配出来。
一些符合的词性的候选词,比如我挑选出名词作为候选词
还可以基于一些统计特征提出候选词,比如TF-IDF(有些时候统计特征也会用在排序中作为特征)
基于一些规则,比如一个句子出现了人名地名,书名号中词,这些很有可能就是关键词
召回其实是一个很重要的部分,在这一步骤,尽可能的召回有用的词汇。我自己的标准是宁可多不能少。如果多了,无非就是增加了资源消耗,但是少了,可能在排序阶段就是无米之炊了。
2.排序

排序阶段,我们可以将方法大致的分为有监督和无监督的方法
2.1无监督抽取关键词

对于无监督,我们分为基于统计和基于图。基于统计就是TF-IDF和各种变种。基于图最常见的就是TextRank。
关键词提取的一个baseline就是 TF-IDF 提取,这种方法效果已经很好。投入产出比很高,我们一般需要去掉常用的停用词,保留重要的词语。
TF-IDF基于统计,易于实现,但是缺点就是没有考虑词与词,词与文档之间的关系。是割裂的。
另一个baseline就是基于图的TextRank, TextRank 由 PageRank 演变而来。
相比于TF-IDF,TextRank考虑了词与词之间的关系(提取思想就是从窗口之间的词汇关系而来),但是缺点是它针对的是单个文本,而不是整个语料,在词汇量比较少的文本中,也就是短文中,效果会比较差。
随着数据量的积累,我们需要把模型更换到有监督模型加上。一般来说,有监督分为两种,一种是看做序列标注,一种是看做二分类的问题。
2.2有监督之二分类

先说二分类问题,比较简单,就是找到词汇的各种特征,去判断这个词汇是不是这个文本的关键词。
我大概罗列一些可能会用到的特征。
位置特征:
使用位置特征是我们基于文本关键词出现的位置是在大量数据的情况下是有规律可言的,比如微博文本中出现在##符号中部分词汇有很大概率就是文本的一个关键词。
是否出现在开头,是否出现在中间部分,是否出现在末尾,出现的位置(具体是第几个单词);相对于整个文本的位置;是否出现在##符号中…
统计特征:
共现矩阵信息;词频;逆词频;词性;词跨度;关键词所在句子的最大长度/最小长度/平均长度;
向量特征:
关键词词向量和文档向量的相似性
2.3有监督之序列标注
关键词的提取,就是一个典型的序列标注的问题。判断句子中关键词的开头中间结尾的位置。
序列标注最基础的就是HMM和CRF方法,但是特征工程比较复杂。
为了解决特征工程复杂的问题,我们使用深度学习模型序列标注。
关于序列标注,大家可以参考我这个文章内容:
工业级命名体识别经验+代码总结

3.新词发现

还会出现一个问题,如果我们使用二分类判定关键词,上述的过程我们都是基于我们的分词器来做的。有可能会出现一些新词,由于分词错误,不能及时的出现在你的候选词库中,比如”爷青结“。
这个时候,我们需要一个新词发现系统,持续不断的补充到词库中,在召回阶段可以提升召回率。
对于新词发现来说,基操就是从文本的自由程度和凝固程度来判断是否是新词,这样的问题就是阈值不好调整从而导致召回和精准不好平衡。
我们还可以通过别的方法离线挖掘实体词补充道词库中,之前有借鉴美团ner的文章实现了一下,效果还不错,在这里,大家可以参考我这个文章:实体库构建:离线大规模新词实体挖掘

搜索资源总结-持续更新

最近看到一个不错的仓库,搜集搜索相关资源,地址在这里:
https://github.com/frutik/awesome-search#types-of-search
我直接fork了

先列取我看到一些将来可能要看的文章,看到就更新,随时更新

搜索相关资源总结
搜索中的 Query 理解及应用1
搜索中的Query扩展技术
电商搜索是如何保证搜索结果不偏离搜索意图的?
Query意图方法(2)- 基于文本分类 - 星轨数据的文章 - 知乎
浅谈Query理解和分析 - 机智的叉烧的文章 - 知乎
搜索引擎的 Query 分析有哪些小技术点?
大话搜索query理解 - 乔一的文章
智能扩充机器人的“标准问”库之Query生成 - 刘聪NLP的文章
Bad Case方法论 - 姚凯飞的文章

query分析 - 知乎
https://www.zhihu.com/topic/19611289

机器学习(十):损失函数 - DataScienceArt的文章 - 知乎
https://zhuanlan.zhihu.com/p/136047113

大话搜索query理解 - 乔一的文章 - 知乎
https://zhuanlan.zhihu.com/p/111904993

从算法理论到工程实践,AI学习路上你遇到了哪些阻碍、走过哪些弯路? - copytang的回答 - 知乎
https://www.zhihu.com/question/358290436/answer/921061372

现阶段各家公司的广告算法使用的主流模型有哪些? - copytang的回答 - 知乎
https://www.zhihu.com/question/352306163/answer/905601365

国内有哪些比较优秀的搜索引擎? - copytang的回答 - 知乎
https://www.zhihu.com/question/278288679/answer/402251102

医疗搜索中的query词权重算法探索
https://mp.weixin.qq.com/s/JCdzhd1wBKIzDkoqW87OAg

搜索广告之自动化创意
https://mp.weixin.qq.com/s/8CN6Ak9skzxXn_qZntJ0FQ

教你如何动态配置词权重,检索系列文章之HDCT论文笔记 - 刘聪NLP的文章 - 知乎 https://zhuanlan.zhihu.com/p/148211196

深度召回在招聘推荐中的挑战和实践

https://mp.weixin.qq.com/s/_2pPa6v2wgb5ht1j_s4Plg

说一说视频搜索

https://mp.weixin.qq.com/s/Sxuv2H9zJLy04BGXeUiF-g

Embedding搜索能代替文本搜索吗?

https://mp.weixin.qq.com/s/cbIqkGg8IwjnSKpEd54wZg

[第9期] 如何识别用户搜索意图之 Query 扩展

https://mp.weixin.qq.com/s/Zulh3iGXZwDJZ9nH4rbhXQ

搜索引擎技术之Query意图分类

https://mp.weixin.qq.com/s/JUjT1Z9yzyUgKA6cTA3VoQ

在query理解中能ALL IN BERT吗?

https://mp.weixin.qq.com/s/G3dr0toZjHH5hyzFBUU-aQ

NLP技术在搜索中的应用–query理解在搜索中的应用

https://mp.weixin.qq.com/s/ypZu9iO07mH5GskDEOuzfw

Query词权重方法(3) - 基于有监督学习

https://mp.weixin.qq.com/s/1EUSz4_r8-j2wIfsHXq-IQ

Query分析三大法宝(2)- 百度结果

https://mp.weixin.qq.com/s/PPBaBd1lgtTHmtXcB9Lg2A

Query分析三大法宝(3)- 片段粒度

https://mp.weixin.qq.com/s/yL23MxTtAS5fF0fFBDDxWw

搜索Query技术体系|方法论

https://mp.weixin.qq.com/s/8wAJOfTV-BuOuVk9Ul5F4Q

Query 理解和语义召回在知乎搜索中的应用

https://mp.weixin.qq.com/s/MAfK4B2F8sPXRLodXkwnmw

什么叫做倒排索引

在搜索场景中,有一个名词非常的频繁叫做“倒排索引”,今天看了一篇参考资料,大致的了解了一下基本原理,记录下来,以备后用。

首先我们来看,搜索场景是这样的:我有海量文本存储在数据库中,同时每次搜索请求,会有query。

基于海量文本,一个比较直观的想法就是建立正排索引。就是我们的每一个文本(有着自己唯一的一个编号)对应着自己文档中的关键词。

形式如下:

doc1: 关键词1,关键词2,关键词3…
doc2:关键词3,关键词4,关键词5…
doc3:关键词1,关键词9,关键词7…

这样的数据存储结构很不利与搜索。假设我们有一关键词“苹果”,我要找到包含苹果的文档,那么我需要对每个文档进行遍历,才可以,这样下来搜索时长太大。

倒排索引是这样的,使用关键词作为存储结构的key,每个关键词对应着包含这个关键词的doc,形式如下:

关键词1: doc1,doc3…
关键词2: doc1…
关键词3: doc1,doc2…

在这种情况下,如果我们去搜索包含“苹果”这个关键词的文档,只需要对key进行索引就可以。

这就是倒排索引,简单讲就是关键词作为key,包含对应关键词的文档集合作为value。

然后我们在讲一个比较细节的东西,整个倒排索引可以分为三个部分:词典,倒排列表,倒排文件

词典:存储自身编号和指向倒排列表的指针

倒排列表:存储包含某个关键词的所有文档列表以及对应关键词出现的次数位置等信息。

倒排文件:倒排文件是存储倒排索引的物理文件

参考文件链接:
https://www.cnblogs.com/zlslch/p/6440114.html

我觉得这个讲的很好

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值