区块链技术博客
www.b2bchain.cn

搞定检索式对话系统的候选response检索–使用pysolr调用Solr

这篇文章主要介绍了搞定检索式对话系统的候选response检索–使用pysolr调用Solr的讲解,通过具体代码实例进行20753 讲解,并且分析了搞定检索式对话系统的候选response检索–使用pysolr调用Solr的详细步骤与相关技巧,需要的朋友可以参考下https://www.b2bchain.cn/?p=20753

本文实例讲述了2、树莓派设置连接WiFi,开启VNC等等的讲解。分享给大家供大家参考文章查询地址https://www.b2bchain.cn/7039.html。具体如下:

点击下方标题,迅速定位到你感兴趣的内容

  • 前言
  • 整体流程
  • Solr和Pysolr
  • 部署Solr
    • 部署
    • 配置IK
    • 配置相似度
  • pysolr使用


前言

说明:讲解时会对相关文章资料进行思想、结构、优缺点,内容进行提炼和记录,相关引用会标明出处,引用之处如有侵权,烦请告知删除。
转载请注明:DengBoCong

构建对话机器人的现有方法中,可以分为 generation-based(生成式)和retrieval-based(检索式),相对于生成式而言,检索式拥有的信息更加丰富,且运行流畅的特点。本篇文章不具体讲解模型,而是来好好阐述关于检索候选回复的实现,比如SMN模型、DAM模型等中,关于检索候选回复的实现。关于SMN模型的论文笔记和实现代码可以参考我的另一篇文章和GitHub,后续我还会对DAM论文和模型写一篇文章。

使用到的工具版本如下:

  • Solr:8.6.3
  • pysolr:3.9.0
  • python:3.7
  • CentOS:7.6
  • Docker:19.03.9

整体流程

我们讲解工具使用之前,首先简要的阐述一下我们的目的,如果已经了解过检索式对话系统或者阅读过相应论文,就不用看了。首先我们知道目的是检索候选回复,用什么检索呢?这个和具体模型结构和需求有关。拿SMN模型为例,利用启发式方法从索引中获取候选response,将前一轮的utterances { u 1 , . . . , u n − 1 } {u_1,…,u_{n-1}} {u1,...,un1} (也就是对话的历史)和 u n u_n un 进行计算,根据他们的tf-idf得分,从 { u 1 , . . . , u n − 1 } {u_1,…,u_{n-1}} {u1,...,un1} 中提取前 5 5 5 个关键字,然后将扩展后的message用于索引,并使用索引的内联检索算法来检索候选response。

模型结构和训练至关重要,但是检索候选回复也是使得整个对话流程实现闭环的关键。我们了解了检索的目的和整体流程,那我们从何实现?方式有很多,可以自行编写一个脚本从数据集中生成一个索引候选数据集(这个是我最开始用的方法,但毕竟没专门研究过检索,所以写的很粗糙,勉强验证功能可以,用作正式使用就不行了),还有一种就是使用现有的检索工具,比如Lucene、Solr、ElasticSearch等等。所以这篇文章就是来讲解部署solr和使用python实现检索(为什么选用Solr?不是说那种工具好坏,而是佛系使用,貌似ElasticSearch现在很火的样子,哈哈哈)。

Solr和Pysolr

Solr它是一种开放源码的、基于 Lucene Java 的搜索服务器,易于加入到 Web 应用程序中。Lucene很底层,从底层代码层面来实现需求,而Solr在其上进行了封装,你如果想要实现脱机检索,那还是使用Lucene吧。Solr 提供了层面搜索(就是统计)、命中醒目显示并且支持多种输出格式(包括XML/XSLT 和JSON等格式)。它易于安装和配置,而且附带了一个基于 HTTP 的管理界面。Solr已经在众多大型的网站中使用,较为成熟和稳定。Solr 包装并扩展了 Lucene,所以Solr的基本上沿用了Lucene的相关术语。更重要的是,Solr 创建的索引与 Lucene 搜索引擎库完全兼容。通过对Solr 进行适当的配置,某些情况下可能需要进行编码,Solr 可以阅读和使用构建到其他 Lucene 应用程序中的索引。此外,很多 Lucene 工具(如Nutch、 Luke)也可以使用Solr 创建的索引。可以使用 Solr 的表现优异的基本搜索功能,也可以对它进行扩展从而满足企业的需要,Solr官网(官方将其和Lucene并列放在一起,嘿嘿嘿,万变不离其宗,看官方文档)。

而Pysolr是基于Python的Solr轻量级封装,它提供了服务器查询并返回基于查询的结果接口。简单来说就是Pysolr封装了Solr的各种http请求,使用起来非常方便,你可以直接从pypi中直接导入(这个就要吐槽一下Pylucene了,不能从pipy直接导入),PySolr官方地址,上面有使用的示例和API,可以自行去看,这里配一张Solr的示意图,方面后面理解:

搞定检索式对话系统的候选response检索--使用pysolr调用Solr

部署Solr

部署

都0202年了,部署服务应用都是用容器了吧,我这里讲解用Docker部署solr,不了解的可以参考我的关于Docker的几篇文章,我这里就不介绍Docker了,默认会就接着往下讲了。

有了docker环境之后,首先先将solr拉下来,我这里拉的是8.6.3的版本(ps:不喜欢拉最新的,因为最新的可能其他的附属库跟不上更新,出问题)

docker pull solr:8.6.3 # 然后启动solr docker run -itd --name solr -p 8983:8983 solr:8.6.3 # 然后创建core核心选择器,我这里因为以SMN模型讲解,所以取名SMN # exec -it :交互式执行容器 # -c  内核的名称(必须) docker exec -it --user=solr solr bin/solr create_core -c smn 

指令具体含义以及core是啥,请自行查阅资料,或者研究一下solr,毕竟先学习基础再来实战。上面构建solr运行容器是简单粗暴且实用的方法,也可以和我一样使用Dockerfile进行构建镜像和容器,Dockerfile内容如下(内容来自docker-solr项目,官方的docker镜像项目):

FROM openjdk:11-jre  LABEL maintainer="The Apache Lucene/Solr Project" LABEL repository="https://github.com/docker-solr/docker-solr"  ARG SOLR_VERSION="8.6.3" ARG SOLR_SHA512="f040d4489118b655bd27451a717c1f22f180c398638d944a53889a1a449e7032b016cecbff1979c2e8bfd51fc037dd613f3b968254001d34fe0e8fc4f6761dcf" ARG SOLR_KEYS="902CC51935C140BF820230961FD5295281436075" # If specified, this will override SOLR_DOWNLOAD_SERVER and all ASF mirrors. Typically used downstream for custom builds ARG SOLR_DOWNLOAD_URL  # Override the solr download location with e.g.: #   docker build -t mine --build-arg SOLR_DOWNLOAD_SERVER=http://www-eu.apache.org/dist/lucene/solr . ARG SOLR_DOWNLOAD_SERVER  RUN set -ex;    apt-get update;    apt-get -y install acl dirmngr gpg lsof procps wget netcat gosu tini;    rm -rf /var/lib/apt/lists/*;    cd /usr/local/bin; wget -nv https://github.com/apangin/jattach/releases/download/v1.5/jattach; chmod 755 jattach;    echo >jattach.sha512 "d8eedbb3e192a8596c08efedff99b9acf1075331e1747107c07cdb1718db2abe259ef168109e46bd4cf80d47d43028ff469f95e6ddcbdda4d7ffa73a20e852f9  jattach";    sha512sum -c jattach.sha512; rm jattach.sha512  ENV SOLR_USER="solr"      SOLR_UID="8983"      SOLR_GROUP="solr"      SOLR_GID="8983"      SOLR_CLOSER_URL="http://www.apache.org/dyn/closer.lua?filename=lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz&action=download"      SOLR_DIST_URL="https://www.apache.org/dist/lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz"      SOLR_ARCHIVE_URL="https://archive.apache.org/dist/lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz"      PATH="/opt/solr/bin:/opt/docker-solr/scripts:$PATH"      SOLR_INCLUDE=/etc/default/solr.in.sh      SOLR_HOME=/var/solr/data      SOLR_PID_DIR=/var/solr      SOLR_LOGS_DIR=/var/solr/logs      LOG4J_PROPS=/var/solr/log4j2.xml  RUN set -ex;    groupadd -r --gid "$SOLR_GID" "$SOLR_GROUP";    useradd -r --uid "$SOLR_UID" --gid "$SOLR_GID" "$SOLR_USER"  RUN set -ex;    export GNUPGHOME="/tmp/gnupg_home";    mkdir -p "$GNUPGHOME";    chmod 700 "$GNUPGHOME";    echo "disable-ipv6" >> "$GNUPGHOME/dirmngr.conf";    for key in $SOLR_KEYS; do      found='';      for server in        ha.pool.sks-keyservers.net        hkp://keyserver.ubuntu.com:80        hkp://p80.pool.sks-keyservers.net:80        pgp.mit.edu      ; do        echo "  trying $server for $key";        gpg --batch --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$key" && found=yes && break;        gpg --batch --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$key" && found=yes && break;      done;      test -z "$found" && echo >&2 "error: failed to fetch $key from several disparate servers -- network issues?" && exit 1;    done;    exit 0  RUN set -ex;    export GNUPGHOME="/tmp/gnupg_home";    MAX_REDIRECTS=1;    if [ -n "$SOLR_DOWNLOAD_URL" ]; then      # If a custom URL is defined, we download from non-ASF mirror URL and allow more redirects and skip GPG step     # This takes effect only if the SOLR_DOWNLOAD_URL build-arg is specified, typically in downstream Dockerfiles     MAX_REDIRECTS=4;      SKIP_GPG_CHECK=true;    elif [ -n "$SOLR_DOWNLOAD_SERVER" ]; then      SOLR_DOWNLOAD_URL="$SOLR_DOWNLOAD_SERVER/$SOLR_VERSION/solr-$SOLR_VERSION.tgz";    fi;    for url in $SOLR_DOWNLOAD_URL $SOLR_CLOSER_URL $SOLR_DIST_URL $SOLR_ARCHIVE_URL; do      if [ -f "/opt/solr-$SOLR_VERSION.tgz" ]; then break; fi;      echo "downloading $url";      if wget -t 10 --max-redirect $MAX_REDIRECTS --retry-connrefused -nv "$url" -O "/opt/solr-$SOLR_VERSION.tgz"; then break; else rm -f "/opt/solr-$SOLR_VERSION.tgz"; fi;    done;    if [ ! -f "/opt/solr-$SOLR_VERSION.tgz" ]; then echo "failed all download attempts for solr-$SOLR_VERSION.tgz"; exit 1; fi;    if [ -z "$SKIP_GPG_CHECK" ]; then      echo "downloading $SOLR_ARCHIVE_URL.asc";      wget -nv "$SOLR_ARCHIVE_URL.asc" -O "/opt/solr-$SOLR_VERSION.tgz.asc";      echo "$SOLR_SHA512 */opt/solr-$SOLR_VERSION.tgz" | sha512sum -c -;      (>&2 ls -l "/opt/solr-$SOLR_VERSION.tgz" "/opt/solr-$SOLR_VERSION.tgz.asc");      gpg --batch --verify "/opt/solr-$SOLR_VERSION.tgz.asc" "/opt/solr-$SOLR_VERSION.tgz";    else      echo "Skipping GPG validation due to non-Apache build";    fi;    tar -C /opt --extract --file "/opt/solr-$SOLR_VERSION.tgz";    (cd /opt; ln -s "solr-$SOLR_VERSION" solr);    rm "/opt/solr-$SOLR_VERSION.tgz"*;    rm -Rf /opt/solr/docs/ /opt/solr/dist/{solr-core-$SOLR_VERSION.jar,solr-solrj-$SOLR_VERSION.jar,solrj-lib,solr-test-framework-$SOLR_VERSION.jar,test-framework};    mkdir -p /opt/solr/server/solr/lib /docker-entrypoint-initdb.d /opt/docker-solr;    chown -R 0:0 "/opt/solr-$SOLR_VERSION";    find "/opt/solr-$SOLR_VERSION" -type d -print0 | xargs -0 chmod 0755;    find "/opt/solr-$SOLR_VERSION" -type f -print0 | xargs -0 chmod 0644;    chmod -R 0755 "/opt/solr-$SOLR_VERSION/bin" "/opt/solr-$SOLR_VERSION/contrib/prometheus-exporter/bin/solr-exporter" /opt/solr-$SOLR_VERSION/server/scripts/cloud-scripts;    cp /opt/solr/bin/solr.in.sh /etc/default/solr.in.sh;    mv /opt/solr/bin/solr.in.sh /opt/solr/bin/solr.in.sh.orig;    mv /opt/solr/bin/solr.in.cmd /opt/solr/bin/solr.in.cmd.orig;    chown root:0 /etc/default/solr.in.sh;    chmod 0664 /etc/default/solr.in.sh;    mkdir -p /var/solr/data /var/solr/logs;    (cd /opt/solr/server/solr; cp solr.xml zoo.cfg /var/solr/data/);    cp /opt/solr/server/resources/log4j2.xml /var/solr/log4j2.xml;    find /var/solr -type d -print0 | xargs -0 chmod 0770;    find /var/solr -type f -print0 | xargs -0 chmod 0660;    sed -i -e "s/"$(whoami)" == "root"/$(id -u) == 0/" /opt/solr/bin/solr;    sed -i -e 's/lsof -PniTCP:/lsof -t -PniTCP:/' /opt/solr/bin/solr;    chown -R "0:0" /opt/solr-$SOLR_VERSION /docker-entrypoint-initdb.d /opt/docker-solr;    chown -R "$SOLR_USER:0" /var/solr;    { command -v gpgconf; gpgconf --kill all || :; };    rm -r "$GNUPGHOME"  COPY --chown=0:0 scripts /opt/docker-solr/scripts  VOLUME /var/solr EXPOSE 8983 WORKDIR /opt/solr USER $SOLR_USER  ENTRYPOINT ["docker-entrypoint.sh"] CMD ["solr-foreground"] 

容器运行情况如下:
搞定检索式对话系统的候选response检索--使用pysolr调用Solr
接下来可以访问:http://xxxxxx:8983/solr/,进入到solr界面,如下:
搞定检索式对话系统的候选response检索--使用pysolr调用Solr
然后点击Core Admin,查看一下自己刚刚创建的Core,如下:
搞定检索式对话系统的候选response检索--使用pysolr调用Solr
然后选择smn就可以使用了,如下:
搞定检索式对话系统的候选response检索--使用pysolr调用Solr
结束了?当然没有,哪有那么简单的事儿,首先我们上面算是基本部署好了solr,但是我们需要进行一些必要的使得我们能更好的使用,比如我们需要对文档进行分词,添加相似度计算类(用于tf-idf计算),接下来就说明如何配置这两个东西。

配置IK

首先是IK,IK Analyzer(中文分词器)是一个开源的,基于java语言开发的轻量级的中文分词工具包。最初,它是以开源项目 Lucene为应用主体的,结合词典分词和文法分析算法的中文分词组件。新版本的IKAnalyzer3.0则发展为 面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。

  • Solr 5以前的可以装上老版本,提取码:g5ib
  • Solr 6使用这个版本
  • Solr 7&8使用这个版本

注意要将IK源码打成JAR包(作为一个老Java,打包还是不难的)。接着将jar包通过传输软件或其它方式传入宿主机的某一文件夹内,然后使用指令将jar包复制到Solr容器的分词包文件夹中:

docker cp ik-analyzer.jar solr:/opt/solr-8.6.3/contrib/analysis-extras/lucene-libs 

查看 Solr 容器在宿主机中数据卷的位置:

docker inspect solr # 找到 Mounts # Destination : 容器里的路径 # Source : 对应宿主机里的路径 

搞定检索式对话系统的候选response检索--使用pysolr调用Solr
将IK分词器配置到 Solr 的核心配置文件中,Source为上面的Mounts中的:

cd {Source}/data/myIKCore/conf/ vim solrconfig.xml 

然后添加如下内容:

# dir	容器存放自带分词JAR包的目录 # regex	JAR包名 <lib dir="${solr.install.dir:../../../..}/contrib/analysis-extras/lucene-libs/" regex="ik-analyzer.jar" /> 

声明中文分词器

vim managed-schema 

找到指定位置添加配置

<!-- IKAnalyzer --> <fieldType name ="text_ik" class ="solr.TextField">     <!-- 索引时候的分词器 -->     <analyzer type ="index" isMaxWordLength ="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/>     <!-- 查询时候的分词器 -->     <analyzer type ="query" isMaxWordLength ="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/> </fieldType> 

重启 Solr 容器

docker restart solr 

选择刚刚创建的核心选择器
搞定检索式对话系统的候选response检索--使用pysolr调用Solr

配置相似度

到了这里,你其实可以直接用了,但是如果使用tf-idf的话,会报错,如下:

org.apache.solr.client.solrj.SolrServerException: No live SolrServers available to handle this request null:java.lang.UnsupportedOperationException: requires a TFIDFSimilarity (such as ClassicSimilarity) 

所以还是需要配置,打开 managed-schema 将下面一行添加进就可以了:

<similarity class="solr.ClassicSimilarityFactory"/> 

pysolr使用

# 创建solr solr = pysolr.Solr(url=solr_server, always_commit=True, timeout=10) # 使用前习惯性安全检查 solr.ping() # 将回复数据添加索引,responses是一个json,形式如:[{},{},{},...],里面每个对象构建按照你回复的需求即可 solr.add(docs=responses) 

接下来进行查询,首先我们提取关键词词,我这里将我的tf-idf方法的代码贴出来,如下:

def get_tf_idf_top_k(history: list, k: int = 5):     """     使用tf_idf算法计算权重最高的k个词,并返回     Args:         history: 上下文语句         k: 返回词数量     Returns: top_5_key     """     tf_idf = {}      vectorizer = TfidfVectorizer(analyzer='word')     weights = vectorizer.fit_transform(history).toarray()[-1]     key_words = vectorizer.get_feature_names()      for i in range(len(weights)):         tf_idf[key_words[i]] = weights[i]      top_k_key = []     tf_idf_sorted = sorted(tf_idf.items(), key=lambda x: x[1], reverse=True)[:k]     for element in tf_idf_sorted:         top_k_key.append(element[0])      return top_k_key 

然后将得到的五个关键词通过query的语法进行组合,得到查询语句,我这里只返回前十个分数最高的候选回复:

query = "{!func}sum(" for keyin tf_idf:     query += "product(idf(utterance," + key + "),tf(utterance," + key + "))," query += ")" candidates = self.solr.search(q=query, start=0, rows=10).docs  #query合起来长这样:{!func}sum(product(idf(utterance,key1),tf(utterance,key1),product(idf(utterance,key2),tf(utterance,key2),...) 

查询回复格式如下:
搞定检索式对话系统的候选response检索--使用pysolr调用Solr
然后检索得到了候选回复就可以喂给模型了。

本文转自互联网,侵权联系删除搞定检索式对话系统的候选response检索–使用pysolr调用Solr

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 搞定检索式对话系统的候选response检索–使用pysolr调用Solr
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们