升级HanLP并使用GPU后端识别发票货物劳务名称

Jean

我的书《图解税收大数据分析》的第一章《发票货物劳务名称识别》用的是国产的开源NLP软件HanLP,当时是HanLP2.0 + Keras2.3 + TensorFlow2.3-GPU +CUDA 10.1+Nvidia 1060 GPU,在台式机上测试的。现在换了台联想拯救者Y9000X笔记本,配了Nvidia RTX 2060 Max-Q GPU,并且HanLP2.1已经支持TensorFlow与PyTorch双后端了,所以在笔记本上配好环境,把HanLP2.1跑起调试好。按照保密规定,只用了1000条比较旧的汇总货劳名称数据在笔记本上测试,并不涉及其它数据及具体的内部数据,应该是没有问题吧。<div>书可能就不修订了,因为学习了Keras R+Python两本书,Keras支持R与Python双语。书中其它涉及深度学习的部分还是用Keras+TensorFlow,因此先另外介绍,源码稍后会在个人的研究网站上提供下载。在R语言中通过Reticulate包调用PyTorch应该也是可以的,并且Rstudio正在开发<a href="https://www.rstudio.com/blog/torch/" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>PyTorch的接口包</a>,<a href="https://torch.mlverse.org/packages/" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>torch for R</a>。</div><div>下面具体介绍一下。<br><div>一、软件安装</div><div>1、升级CUDA及cudnn。</div><div>之前在我的<a href="http://jeanye.cn" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>Linux虚拟主机</a>上已经配好了HanLP2.1的环境,<a href="https://jeanye.cn:8443/fpbz/" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>演示</a>用的是10条固定的汇总货劳名称数据,配套运行的是Keras2.6.0+TensorFlow2.6.0 CPU版及PyTorch 1.11.0 CPU版,从二者官网的资料可知,<a href="https://tensorflow.google.cn/install/source_windows#gpu" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>tensorflow-gpu-2.6.0支持的是cuda 11.2版</a>,<a href="https://download.pytorch.org/whl/torch/" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>torch 1.11.0支持的是cuda 10.2与11.3</a>,似乎不能同时使用。后来搜到了<a href="https://blog.csdn.net/xiao_yun_zi/article/details/124046964" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>这个帖子</a>,看到有人在cuda 11.3上跑起了tensorflow-gpu-2.6.0,就决定用cuda 11.3同时跑起两个后端,好测试比较。具体安装看帖子即可,不再赘述,配套的是cudnn 8.2.1。这两个软件大概需要5G的硬盘空间,如果C盘不够,可以先把cuda的旧版本卸掉,比如cuda 10.1,cuda的多个版本是可以并存的,设置系统的环境变量指向选用的版本即可。</div><div>2、安装tensorflow-gpu-2.6.0,全部配套库大概要2G。</div><div>按照HanLP的<a href="https://hanlp.hankcs.com/docs/install.html" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>在线安装文档</a>,pip install hanlp[tf] 就会把配套的tensorflow及pytorch装好,在linux 虚拟主机上是这样装的,不过笔记本上试了一下,自动装的是CPU版,所以要自己先装好tensorflow-gpu-2.6.0及pytorch 1.11.0+cudatoolkit11.3,再用pip install hanlp 安装。</div><div>1)打开conda prompt命令行窗口,先建个anaconda3虚拟环境hanlp,不影响原来的python运行环境,用python最新的3.9版。</div><div>> conda create -n hanlp python=3.9</div><div>2)切换到新建的虚拟环境</div><div>> conda activate hanlp</div><div>3)安装tensorflow-gpu-2.6.0,conda解决不了版本兼容的时候就直接用pip。</div><div>> pip install tensorflow-gpu==2.6.0</div><div>4)安装keras 2.6.0,上面会自动安装keras 2.8.0,卸掉再装2.6.0。</div><div>>pip uninstall keras</div><div>>pip install keras==2.6.0</div><div>3、安装PyTorch 1.11.0,大概也要2G的空间。</div><div>按照<a href="https://pytorch.org/" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>官网的指示</a>装即可。</div><div>>conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch</div><div>4、安装Spyder,最新的是5.3,<a href="https://blog.csdn.net/wangwy_hoveringeagle/article/details/121177790" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>参阅资料</a>。</div><div>笔记本上用Spyder来调试Python程序,每个conda虚拟环境都要装自己的Spyder副本。因为前面装了低版本的tensorflow及配套,conda install解决不了版本冲突,直接用pip install。这样装的Spyder不会在菜单上加入启动的快捷方式,用conda装的就会,所以也可以建立虚拟环境后用conda先装Spyder。</div><div>> pip install spyder</div><div>5、安装hanlp,tensorflow需要用fasttext。</div><div>>pip install fasttext</div><div>>pip install hanlp<br></div><div>6、注意安装的过程中ipython是8.3.0->7.33,ipykernel是6.9.1->6.13.0,textdistance是4.2.1->4.2.2,如果安装的过程中有些包被替换后造成Spyder启动不了,要换回原来的版本。安装过程要注意pip提示的替换与版本冲突,最好拷贝保存一下安装日志。</div><div>7、安装igraph 0.9.8,最新的版本有bug,以及需要的作图库,用conda install也可以。</div><div>>pip install matplotlib</div><div>>pip install pycairo</div><div>>pip install igraph==0.9.8</div><div>Python igraph作图时显示不了标签,可以按<a href="https://stackoverflow.com/questions/30640489/issue-plotting-vertex-labels-using-igraph-in-ipython" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>这个贴子</a>的指示修改<a href="http://jeanye.cn/fp/igraph.drawing.__init__.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>~\igraph\drawing\__init__.py</a>解决。</div><div>8、Jupyter Notebook中添加新的虚拟环境,参考<a href="https://baijiahao.baidu.com/s?id=1679519911553977352&wfr=spider&for=pc" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>帖子</a>。有些notebook程序可能要用Jupyter来运行,比如HanLP2.1语法分析结果的pretty_print()是HTML格式(<a href="http://jeanye.cn/fp/HanLP2.1-PyTorch-GPU-FPHLMC.ipynb" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>PyTorch-GPU Notebook 源码</a>,<a href="http://jeanye.cn/fp/HanLP2.1-TensorFlow-GPU-FPHLMC.ipynb" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>TensorFlow-GPU Notebook 源码</a>),要用Jupyter Notebook运行才可以打印出来,或者在Linux主机上习惯了用Jupyter。在conda hanlp 虚拟环境的command prompt下运行,其中hanlp是虚拟环境的名字:</div><div>>python -m ipykernel install --user --name hanlp --display-name "hanlp"</div><div><br></div><div>其它运行时提示缺失的包,按要求装上即可。可以运行<a href="http://jeanye.cn/fp/kerasTest3.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>这个程序</a>测试cuda+cudnn+tensorflow+keras的安装,运行<a href="http://jeanye.cn/fp/pytorch-Test.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>这个程序</a>来测试pytorch+cudatoolkit+hanlp的安装。</div></div> 二、HanLP2.1 GPU基本测试<div>本篇的Python源码可以到我的研究网站上<a href="http://jeanye.cn/fp/" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>下载</a>。<br><div>1、<a href="http://jeanye.cn/fp/HanLP2.1-Test-TencorFlow.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>TensorFlow测试</a>,LARGE_ALBERT_BASE是tensorflow模型,这是<a href="https://github.com/hankcs/HanLP/tree/doc-zh/plugins/hanlp_demo/hanlp_demo/zh" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>HanLP Github主页上的例子</a>。</div><div>import hanlp<br></div><div>tokenizer = hanlp.load(hanlp.pretrained.tok.LARGE_ALBERT_BASE)<br>text = 'NLP统计模型没有加规则,聪明人知道自己加。英文、数字、自定义词典统统都是规则。'<br>print(tokenizer(text))</div><div>2、<a href="http://jeanye.cn/fp/HanLP2.1-Test-PyTorch.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>PyTorch测试</a>。这个pipeline里用的都是pytorch模型。</div></div><div>import hanlp<br></div><div>HanLP = hanlp.pipeline() \<br> .(hanlp.utils.rules.split_sentence, output_key='sentences') \<br> .(hanlp.load('FINE_ELECTRA_SMALL_ZH'), output_key='tok') \<br> .(hanlp.load('CTB9_POS_ELECTRA_SMALL'), output_key='pos') \<br> .(hanlp.load('MSRA_NER_ELECTRA_SMALL_ZH'), output_key='ner', input_key='tok') \<br> .(hanlp.load('CTB9_DEP_ELECTRA_SMALL', conll=0), output_key='dep', input_key='tok')\<br> .(hanlp.load('CTB9_CON_ELECTRA_SMALL'), output_key='con', input_key='tok')<br></div><div>HanLP('2021年HanLPv2.1为生产环境带来次世代最先进的多语种NLP技术。阿婆主来到北京立方庭参观自然语义科技公司。')<br></div><div>3、<a href="http://jeanye.cn/fp/demo_pipeline_tf_pt.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>TensorFlow与PyTorch混合使用</a>,这时要先加载TensorFlow的模型,再加载PyTorch的模型,这是开发者何晗的答复,见<a href="https://github.com/hankcs/HanLP/blob/doc-zh/plugins/hanlp_demo/hanlp_demo/zh/demo_pipeline.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>HanLP 主页上的例子</a>。否则就会报错说找不到tensorflow安装,这个应该是底层二者兼容性上的bug。</div><div>ImportError:<br>TFAutoModel requires the TensorFlow library but it was not found in your environment. Checkout the instructions on the<br>installation page: https://www.tensorflow.org/install and follow the ones that match your environment.<br></div> 三、升级到2.1后货劳名称识别模型 <a href="http://jeanye.cn/fp/HanLP-Serialized.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>TensorFlow CPU后端的实现</a><br><div>这个版本是2020年秋天用HanLP2.0完成的,2021年6月升级到HanLP2.1 Alpha并调试好,具体源码见上面的链接。主要是用户自定义词典按HanLP的规范用Trie树实现。然后定义了一个流水线pipeline来实现自定义词提取、分词、词性标注、合并结果、语法分析、语义分析。这是逐个顺序处理的CPU版,处理单条货劳名称,读懂这个版本,后面其它版本就很好懂了。</div><div>parser = hanlp.pipeline() \<br> .(split_sents, output_key=('parts', 'offsets', 'words'), trie=trie) \<br> .(tokenizer, input_key='parts', output_key='tokens') \<br> .(tagger,input_key='tokens', output_key='postags') \<br> .(merge_parts, input_key=('tokens', 'postags', 'offsets', 'words'), output_key='merged') \<br> .(syntactic_parser, input_key='merged', output_key='syntactic_dependencies')<br></div><div>1、split_sents()函数用自定义词典先提取自定义词,剩下的部分传给分词器tokenizer分词。</div><div>2、tokenizer分词传给tagger标注词性。</div><div>3、merge_parts()函数把提取的自定义词与剩余部分的分词与词性标注结果合并到一起。</div><div>4、对合并的结果作语法分析或语义分析。</div><div>具体可以参考HanLP 主页上<a href="https://github.com/hankcs/HanLP/blob/doc-zh/plugins/hanlp_demo/hanlp_demo/zh/tf/demo_cws_trie.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>tensorflow自定义词典的例子</a>。升级到HanLP2.1时对<a href="http://jeanye.cn/fp/hanlp_trie.trie.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>hanlp_trie.trie.py</a>增加 parse_longest2()函数以返回自定义词与词性,原来的parse_longest()函数只返回自定义词。</div><div>自定义词典示例见<a href="http://jeanye.cn/fp/HanLP-User-Dictionary-demo.csv" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>此处</a>,这里有<a href="http://jeanye.cn/fp/hlhz-utf8-100.csv" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>100条货劳名称数据</a>可以用于测试,仅包含名称数据。</div><div>5、然后是函数syntacticParse()提取语法分析的结果,getTree()将语法分析的结果转换为igraph的图。semanticParse()提取语义分析的结果,getTree2()将语义分析的结果转换为igraph的图。</div><div>6、然后是函数get_hlmc()根据业务规则遍历图去提取货劳名称。主控函数parseHlmc()则遍历所有数据,根据业务经验与统计决定哪些用语法分析,哪些用语义分析去处理。</div><div>7、函数preprocess()是对货劳名称先作清理,以尽量删除无用的文本。</div><div>HanLP2.0到2.1 tensorflow后端的主要变化就是语法分析与语义分析返回结果的格式,2.1 Alpha与2.1 Beta版之间这些部分则没有变化。</div> 四、货劳名称识别模型<a href="http://jeanye.cn/fp/HanLP-GPU-Parallel-TensorFlow.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>TensorFlow GPU后端的实现</a><br><div>GPU后端版为了用GPU对货劳名称列表并行处理,先用一个函数merge_sents()把传入的货劳名称列表以特定分隔符"~~"合并成单个句子,然后象上面串行处理一样用Trie树自定义词典提取自定义词,剩下部分传给后面分词与标注词性,然后合并结果,再用函数split_pos()按特定分隔符拆分回多个货劳名称(和结果)列表,再传到后面作语法分析或语义分析。这个流水线中TensorFlow预训练模型部分在GPU中都是并行的,难度是把CPU中运行的自定义词的提取部分嵌入流水线中,所以使用了函数merge_sents()与split_pos(),以及特定分隔符"~~",它也作为自定义词典的第一个词放在词典中,因为它是约定的分隔符,分词时要识别为一个词,作这样的约定是因为它很少出现在这个语境中。下面是部分自定义词典,CSV格式是“词条,词性”,每个词条一行:</div><div>~~,W<br>线路板,NN<br></div><div>这个办法是作者<a href="https://github.com/hankcs" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>何晗</a>教的,的确解决了问题。</div><div># 定义流水线,引用自定义词库,分词、词性标注、合并、依存句法分析/语义依存分析<br>parser3 = hanlp.pipeline() \<br> .(merge_sents, output_key="sents" ) \<br> .(split_sents, input_key="sents", output_key=('parts', 'offsets', 'words'), trie=trie) \<br> .(tokenizer, input_key='parts', output_key='tokens') \<br> .(tagger,input_key='tokens', output_key='postags') \<br> .(merge_parts, input_key=('tokens', 'postags', 'offsets', 'words'), output_key='merged') \<br> .(split_pos, input_key="merged", output_key="splits") \<br> .(syntactic_parser, input_key='splits', output_key='dependencies') <br></div><div>然后对前面的串行版作了优化,先把所有货劳名称分为4个子集,无自定义词非并列模式、无自定义词并列模式、有自定义词非并列模式、有自定义词并列模式,这部分是串行处理的,不涉及深度学习预训练模型,也很快。每个子集定义一个不同的pipeline去处理,并列模式的用语义分析,非并列模式的用语法分析,这是根据我自己观察暂定。无自定义词非并列模式的占大多数,它不需要上面的方法去嵌入提取自定义词的环节,流水线简单一点就快很多。</div><div>然后定义了函数ParseGPU()调用相应的流水线去并行完成分析,再顺序处理返回的结果。结果的处理在CPU,串行也很快。</div><div>这里合并了从语法树或语义图分析返回结果生成igraph图的函数getTree(),其它提取货劳名称的函数get_hlmc(),预处理函数preprocess()与前面是一样的。</div><div>GPU版的处理速度大概是CPU版的7~10倍,随GPU的算力而不同。1000条的测试集15秒就处理完了,CPU版大概每条要1秒。具体用的时候要根据GPU的算力合理划分每块处理的条数(这里是1000条),以充分利用GPU的算力。<a href="https://jeanye.cn:8443/fpbz/" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>Shiny APP标注示例</a>是演示性质,或用于公众标注,处理单条数据,实际使用中可能更多的是后台批量处理,预分析和预标注,所以GPU版也是非常有用的。</div> 五、升级到2.1后货劳名称识别模型<a href="http://jeanye.cn/fp/HanLP2.1-FPHLMC.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>PyTorch CPU后端的实现</a><br><div>PyTorch是HanLP2.1新增的后端,自定义词典的使用比TensorFlow后端要简单,速度也要快一点。不过选用什么样的后端主要还是取决于自己的使用习惯与模型的性能,用开tensorflow+keras的大可以继续用tensorflow,习惯pytorch的现在也可以用HanLP了。至于性能,主要是看准确率,在没有训练好发票专用的模型或自定义词典前,二者是很难比较的。所以还是看用户的习惯与偏好吧。</div><div>PyTorch后端分词与词性标注用不同的用户自定义词典,分词用的是集合,不包含词性,词性标注用的是字典,包含词性,都用前面TensorFlow后端的用户自定义词典,加载时稍作处理即可。然后它们是作为分词器或标注器的属性挂载的,所以不用嵌入后面的流水线中,流水线就变简单了。</div><div># 从CSV文件装入用户自定义词典<br>types = {"word":str,"pos":str}<br>dicZdy = pd.read_csv('D:/temp/data/增值税自开票2017/词汇导入demo-HanLP.csv', sep=',', header=None, dtype=types, encoding="GBK")<br># 词性标注字典是个字典<br>dictPos = dict(collections.OrderedDict(zip(dicZdy.iloc[:,0],dicZdy.iloc[:,1])))<br># 分词字典是个集合<br>dicTag = set(dicZdy.iloc[:,0])<br>tokenizer.dict_force = dicTag<br></div><div>tagger.dict_tags= dictPos<br></div><div><br></div><div>pipeline = hanlp.pipeline() \<br> .(tokenizer, output_key='tokens') \<br> .(tagger, input_key='tokens', output_key='part_of_speech_tags') \<br> .(syntactic_parser, input_key='tokens', output_key='syntactic_dependencies') \<br> .(semantic_parser, input_key='tokens', output_key='semantic_dependencies')<br></div><div><br></div><div>比如上面这个流水线,分词、词性标注、语法分析、语义分析,一次全做了。tokenizer与tagger已经挂载了用户自定义词典。然后如果传入的是单条货劳名称,它是串行的,如果传入的是多条货劳名称列表,就是并行的,CPU版也支持并行,因为现代CPU有多核。</div><div>分词挂载用户自定义词典可以看这个<a href="https://github.com/hankcs/HanLP/blob/doc-zh/plugins/hanlp_demo/hanlp_demo/zh/demo_custom_dict_stl.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>例子</a>,词性标注挂载用户自定义词典可以看这个<a href="https://github.com/hankcs/HanLP/blob/doc-zh/plugins/hanlp_demo/hanlp_demo/zh/demo_pos_dict.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>例子</a>。</div><div>与TensorFlow后端相比,语法分析与语义分析返回的格式与字段稍有不同,所以语法分析函数semanticParse()与转换为igraph图的函数getTree(),语义分析函数semanticParse()与getTree2()要修改一下。其它与TensorFlow后端版相同。主控函数parseHlmc()稍为作了一点修改。</div> 六、货劳名称识别模型<a href="http://jeanye.cn/fp/HanLP-GPU-Parallel-PyTorch.py" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>PyTorch GPU后端的实现</a><br><div>GPU版先把数据分为并列模式与非并列模式两个子集,并列模式用语义分析的流水线,非并列模式用语法分析的流水线。因为PyTorch版没有流水线嵌入Trie树用户自定义词典提取用户自定义词的环节,所以只需要划分为两个子集,定义两条流水线即可。</div><div>然后定义了ParseGPU()函数来并行的处理一个子集。与TensorFlow版一样,深度学习预训练模型处理部分是并行的,结果处理部分是顺序串行的。因为PyTorch后端语法分析与语义分析结果的格式差异较大,没有合并生成igraph图的函数getTree()与getTree2()。</div><div>PyTorch语义分析返回包含环路的图的几率比TensorFlow版要高,所以从图中提取货劳名称的算法get_hlmc()稍为完善了一下,广度优先遍历图时,检测结点是否已经遍历过,不重复遍历,以免在环路中循环导致Python递归堆栈溢出。</div><div>存在环路情况下PyTorch语义分析返回无根结点图的机会也比TensorFlow版要高,这种情况下get_hlmc()就直接返回None,即无法提取。</div><div>这可能是预训练模型在特定场景下的小问题,随着模型的迭代更新,又或者训练发票专用模型或用户自定义词典的完善,应该可以得到较好的解决。</div><div>按照<a href="https://hanlp.hankcs.com/docs/annotations/sdp/semeval16.html#" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>CSDP的定义</a>,语义图应该是有向无环图,这一点HanLP PyTorch后端可能需要继续完善一下。</div><div>PyTorch GPU后端完成1000条的处理只要7秒多,速度比TensorFlow GPU后端快1倍。主要的原因大概是用户自定义词典的处理?在TensorFlow GPU后端,自定义词的提取是嵌入流水线中,串行执行,PyTorch GPU后端自定义词典是作为分词器与词性标注器的属性,在流水线中并行执行,并且流水线也简化了。也许TensorFlow 版用户自定义词典的实现也可以参考PyTorch版改进一下,简单、快速。</div><div>PyTorch GPU后端是目前测试过的最快速度了。<br></div> 七、离线运行HanLP2.1<div>HanLP2.1的一个好处是分为RESTful与Native两种API来实现,RESTful API调用云端的服务,Native API安装到本地运行,本文用的是Native API,可以部署在内网的机器上运行,数据不会发送到云端。在一些数据敏感的应用场景中,这是个优势。</div><div>HanLP2.1的预训练模型可以下载到本地,然后以后从本地加载即可,Linux上是放在~/.hanlp目录下,Windows上是放在%appdata%/hanlp目录下,然后也可以通过<a href="https://hanlp.hankcs.com/docs/configure.html#customize-hanlp-home" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>设置环境变量HANLP_HOME</a>来指定。</div><div>2.1版的预训练模型,不少都引用了<a href="https://huggingface.co/" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>Hugging Face</a> 的Transformers模型,所以即使预训练模型已经下载到本地%HANLP_HOME目录了,每次载入时还会联网加载所引用的Transformers模型。通过<a href="https://hanlp.hankcs.com/docs/install.html" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>设置环境变量TRANSFORMERS_OFFLINE=1</a>,HanLP2.1会在本地的cache中加载,在windows上,在C:\Users\user_name\.cache\huggingface\transformers目录下,也就是说,要连线加载一次。Linux上就是在~/.cache/huggingface/transformers目录下。</div><div>export TRANSFORMERS_OFFLINE=1<br></div><div>所以如果是部署在内网的服务器上,可以在联网的机器上先下载好,再拷贝到服务器相应的目录即可。</div><div>HanLP的预训练模型可以直接从它的国内<a href="https://od.hankcs.com/hanlp/data/" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>镜像站点</a>下载,Hugging Face Transformers模型可以参考<a href="https://www.youtube.com/watch?v=bnaCKP6mLZI" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>这个帖子</a>下载另存。</div><div>也可以修改HanLP2.1预训练模型config.json中对Hugging Face Transformers模型的引用,直接指向其本地的存储路径,比如<a href="http://jeanye.cn/fp/dep.ctb9_dep_electra_small_20220216_100306.config.json" target="_blank" class="link"><i class="iconfont icon-iconfontlink"> </i>这个例子</a>,这样便于集中存放离线运行的预训练模型。</div><div> "transformer": "D:/HanLP/transformers/hlf/chinese-electra-180g-small-discriminator",<br></div> 八、发票货劳名称识别算法模型设计小结<div>最后总结一下本篇发票货劳名称识别算法模型的设计思路,以便将来优化算法与业务。</div><div>1、发票货劳名称是自由书写的自然语言,所以没有固定的格式,即使将来可以规定某些规范,但历史数据已无法改变,算法模型要有效处理历史数据。</div><div>2、货劳名称显然是一个名词,发票上书写的货劳名称是个名词性的短语,发票货劳名称识别就是从短语中提取最可能的名词。</div><div>3、货劳名称短语的上述特性决定了用NLP技术对货劳名称分词,然后重建短语的内部结构,再开发提取算法是条可行的路径。</div><div>4、基于深度学习技术训练的NLP预训练模型,在海量语料库上训练,配合发票专用词典,可以有效的对发票短语进行分词与词性标注,进行词性标注是因为要提取的是名词。</div><div>5、基于海量语料库上训练的深度学习预训练模型,可以通过依存句法分析与语义依存分析等,有效重建发票短语的内部结构。</div><div>6、在重建内部结构的基础上,可以开发有效的算法提取最可能的货劳名称。本篇中就是用igraph重建语法树或语义图,然后从根结点开始用广度优先算法遍历提取名词。海量语料库训练重建的语法树或语义图有最大的合理性统计学几率,在这个意义上,越靠近根结点,即层级越高,其自然语义上为精确货劳名称的几率越高,这个过程相当于对短语中的名词重新排序。</div><div>7、发票短语有明显的业务统计规律,可以把业务统计规律应用于上述遍历提取的过程。比如本文的get_hlmc()函数中已经应用了一些我观察总结的业务规律,如非并列结构后面的名词优先等,这样可以大幅提高算法的准确率。</div><div>8、同样从业务观察中总结明显规律的预处理过程可以有效提高识别的准确率。本篇用preprocess()函数先清理无关的文本,如括号说明等。</div><div>9、发票货劳名称中有大量日常语料中不常见的专用名词,影响分词的准确性,所以需要建立发票专用词典或训练专用的模型。<br></div><div>10、货劳名称的出现频率分布极不平均,有效处理高频的少数货劳名称就可以解决大部分问题。本算法模型通过从发票明细汇总货劳名称将数据量压缩到原始数据的10%左右,然后通过频数排序,建立用户自定义词典优先处理前1000条的数据,仅仅200多条的词条,前1000条已经处理了覆盖3142万条原始数据29.7%的记录与48.6%的金额,准确率超过95%,效果非常不错了。</div><div>11、在算法的迭代进化中,可以制定一些业务规则,比如一物开一票等规范将来的开票业务,因为在发票大数据分析中,发票承载的交易对象应该是唯一且明确的,以使将来的发票大数据处理更规范准确。杂货类等并列的小品种可用类别总称加以规范,比如“文具(订书机、回形针、铅笔)”等。在纸质发票的时代开票量的增加可能会有一定的抵触,但如果是电子发票,应该没有大的问题,在开始推行就加以规范是比较合适的。IT应用要优化业务,而不是复现业务。</div> 文章写到这里就结束了,这个GPU上的算法模型是2020年秋天完成的,那时山上的叶子已经开始黄了。我并不打算把它锁在抽屉里,也不打算申请专利加以使用上的限制,因为是工作的成果,并且也希望它对全国税务系统的工作有所帮助。上一个虎年我为广东地税的在线发票引进了手机扫码查验,并公开源码教会了三大电信运营商,我相信自己做了正确的选择,现在几乎每个中国人天天都在扫各种二维码。这个虎年我还是决定公开这项研究的源码,我相信这是个正确的决定。以后人们如果引用了这个算法模型的源码或逻辑或思想,可以称它为“黄叶”算法,就像有向图最小树形图的“朱刘”算法一样,那就是对引用算法来源的说明,也是对知识产权以及后面的贡献和付出的尊重。<div>谢谢读者诸君。</div>