查看原文
其他

Stata:调用百度文心千帆大模型进行文本分析与文本主要内容提取 —— 银保监行政处罚决定书文本提取

RStata RStata 2023-10-24

为了让大家更好的理解本文的内容,欢迎各位培训班会员参加明晚 8 点的直播课 「Stata:调用百度文心千帆大模型进行文本分析与文本主要内容提取 —— 银保监行政处罚决定书文本提取」


今年年初给大家分享过一份 银保监本级、分局本级、机关行政处罚信息数据(截止 2023 年 2 月 13 日) 数据。实际上这些行政处罚信息包含多种形式的,大体上是处罚决定书和处罚信息公开表,由于处罚决定书的内容比较混乱,难以整理成表格数据,所以就没有整理。

百度文心一言最近开放了 API 接口,于是我就想是不是可以调用文心一言的 API 接口自动处理这些处罚决定书文本来提取主要信息。

申请文心千帆大模型内测

首先我们需要申请文心千帆大模型内测的资格,网址是这个:https://cloud.baidu.com/product/wenxinworkshop?track=ae35a3396ecb3afe68e19773b574a198b11ace12bfeed89c

然后在文心千帆控制台创建应用:https://console.bce.baidu.com/ai/#/ai/wenxinworkshop/overview/index

由于本课程讲解的 ERNIE-Bot 模型的调用是需要付费的,所以可以先给自己的账户充点钱,10 块钱即可。

价格如下,调用一次的价格差不多 1 分钱左右:

创建应用后可以应用列表中可以看到自己的 API Key 和 Secret Key:https://console.bce.baidu.com/ai/#/ai/wenxinworkshop/app/list,注意保存好。

大家注意把课程代码中的 API key 和 Secret Key 替换成自己的。

最后再阅读 API 使用文档即可使用了:https://cloud.baidu.com/doc/WENXINWORKSHOP/s/jlil56u11 。本课程中将以 ERNIE-Bot 的使用为例进行讲解。

银保监处罚决定书关键信息提取

附件中我准备了一些 doc 格式的 word 文档,是银保监处罚决定书,例如 1091075.doc 文件:

我们的目标是从中提取如下变量:

  • 年份
  • 标题
  • 处罚决定书文号
  • 主要违法违规事实或案由
  • 处罚依据
  • 处罚决定
  • 作出处罚决定的机关名称
  • 作出处罚决定的日期
  • 被处罚个人姓名
  • 被处罚单位名称
  • 被处罚单位的法定代表人或主要负责人姓名
  • 发布时间

因为提供的示例数据是 doc 文件,Stata 无法处理,所以我们首先使用 R 语言的 textreadr 包进行读取转换:

# 安装 textreadr 包
# install.packages("pacman")
# pacman::p_load_gh("trinker/textreadr")
library(textreadr)
read_dir("doc") -> docdf 

library(tidyverse)
docdf %>% 
  as_tibble() %>% 
  mutate(content = str_squish(content),
         length = str_length(content)) %>% 
  arrange(desc(length)) -> docdf

# 保存
docdf %>% 
  haven::write_dta("docdf.dta")

不过有些 doc 文件似乎有问题,无法处理。我们把这些文件筛选出来,下面就完全使用 Stata 了。

首先提取所有的文件名:

*- 创建 errordoc 文件夹
cap mkdir "errordoc" 
local files: dir "doc" files "*.doc"
local n: word count `files' 
clear 
set obs `n'
gen document = ""
forval i = 1/`=_N' {
 local file: word `i' of `files'
 replace document = `"`file'"' in `i'
}
replace document = subinstr(document, ".doc""", .)
save "alldocuments"replace 

读取成功的文件存放在 docdf.dta 文件夹中,和 alldocuments merge 起来即可筛选出读取失败的:

use docdf, clear 
merge 1:1 document using alldocuments 
keep if _m == 2

*- 循环把这些文件 copy 到 errordoc 里面
forval i = 1/`=_N' {
 copy "doc/`=document[`i']'.doc" "errordoc/`=document[`i']'.doc"replace 
}

这些文件可以使用 word 打开,另存为 doc 文件再放回到 doc 文件夹中。然后再重复最初的 R 代码即可。这里因为时间关系,我就不再手动一个个操作了。

use docdf, clear 
*- 由于文心千帆模型的参数长度限制,这里长度超过 2000 的(加上其他的文本,差不多不能超过 1800)不能一次解析,可以手动缩短后再解析。

*- 这里我们选择 10 条为例进行解析
drop if length >= 1800 
keep in 1/10 

可以把这 10 个文件单独拿出来方便等下比对:

cap mkdir "usedoc"
forval i = 1/`=_N' {
 copy "doc/`=document[`i']'.doc" "usedoc/`=document[`i']'.doc"replace 
}
save usedocdf, replace 

ERNIE-Bot 模型接口的使用

首先创建一个对话:

curl 不是 Stata 自带的命令,而是一个 DOS/Shell 命令,Win10 之后的系统或者 Mac 系统都可以直接使用,旧版本的 Windows 系统需要自行安装,具体可以百度搜索下教程。

*- 注意这里需要使用自己的 API key 和 Secret key 
!curl 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=PVodUnhWFKaORmyBHVGFuwZK&client_secret=ysmvX70jAKPABxg5bEeSEABZyBGAfjIx' -o session.json 

运行上面的代码后,进程信息会被保存到 session.json 文件里面,下面我们使用 insheetjson 处理提取:

*- 使用 insheetjson 处理这个 json 文件,提取 access_token 
*- 查看响应
insheetjson using session.json, showr flatten 
*> Response from server:
*>         refresh_token = 25.2bb9649d828da2337a74e797c24fac66.315360000.2005638177.282335-36641548
*>         expires_in = 2592000
*>         session_key = 9mzdWuuW5A+0pBOuEtucUaqszV9TUlL916McJ7f7h6fUBLUH7O+9lTi\/cGZoMW6xLnu5jZqPp58xHIuLBp5REXAxz
*> > PQIhg==
*>         access_token = 24.a77d6a26c0f1846bb616530931a1077e.2592000.1692870177.282335-36641548
*>         scope = public brain_all_scope easydl_mgr easydl_retail_mgr ai_custom_retail_image_stitch ai_custom_test
*> > _oversea easydl_pro_job easydl_pro_mgr ai_custom_yiyan_com ai_custom_yiyan_com_eb_instant wenxinworkshop_mgr a
*> > i_custom_yiyan_com_bloomz7b1 ai_custom_yiyan_com_emb_text wise_adapt lebo_resource_base lightservice_public he
*> > tu_basic lightcms_map_poi kaidian_kaidian ApsMisTest_Test\u6743\u9650 vis-classify_flower lpq_\u5f00\u653e cop
*> > _helloScope ApsMis_fangdi_permission smartapp_snsapi_base smartapp_mapp_dev_manage iop_autocar oauth_tp_app sm
*> > artapp_smart_game_openapi oauth_sessionkey smartapp_swanid_verify smartapp_opensource_openapi smartapp_opensou
*> > rce_recapi fake_face_detect_\u5f00\u653eScope vis-ocr_\u865a\u62df\u4eba\u7269\u52a9\u7406 idl-video_\u865a\u6
*> > 2df\u4eba\u7269\u52a9\u7406 smartapp_component smartapp_search_plugin avatar_video_test b2b_tp_openapi b2b_tp_
*> > openapi_online smartapp_gov_aladin_to_xcx
*>         session_secret = 0da8132cd827743b94d1a8b827d98a44

access_token 就是我们想要提取的对话 id,相当于我们创建了一个聊天室。

*- 使用 insheetjson 处理这个 json 文件,提取 access_token 
*- 查看响应
insheetjson using session.json, showr flatten 

clear all 
gen str100 access_token = ""
insheetjson access_token using session.json, column("access_token"
compress 
local access_token = "`=access_token[1]'"
di "`access_token'" 
*> 24.a77d6a26c0f1846bb616530931a1077e.2592000.1692870177.282335-36641548

然后就可以循环提问、保存结果了:

use usedocdf, clear 
*- 去除所有的双引号
replace content = subinstr(content, `"""'"", .)

*- 循环提问、保存结果
cap mkdir "json"
forval i = 1/10 {
 *- 发起提问
 !curl -XPOST 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token=`access_token'' -d '{"messages": [{"role":"user","content":"阅读这段话:\"`=content[`i']'\",提取年份、标题、处罚决定书文号、主要违法违规事实或案由、处罚依据、处罚决定、作出处罚决定的机关名称、作出处罚决定的日期、被处罚个人姓名、被处罚单位名称、被处罚单位的法定代表人或主要负责人姓名 、发布时间、罚款金额信息。结果使用文本表示,用数字对结果进行编号"}]}' -o json/`=document[`i']'.json
}

为了便于处理,所有的提问我都采取这种格式:

阅读这段话:"......",提取年份、标题、处罚决定书文号、主要违法违规事实或案由、处罚依据、处罚决定、作出处罚决定的机关名称、作出处罚决定的日期、被处罚个人姓名、被处罚单位名称、被处罚单位的法定代表人或主要负责人姓名 、发布时间、罚款金额信息。结果使用文本表示,用数字对结果进行编号

经过多次测试,这样得到的结果会很容易处理。

然后再循环解析得到的 json 文件,提取 result 结果,首先测试一个:

*- 提取结果
insheetjson using json/1055966.json, showr flatten 

*> Response from server:
*>         id = as-uj5e4nkn5d
*>         object = chat.completion
*>         created = 1690220163
*>         result = 1. 年份:2022\n2. 标题:合众人寿保险股份有限公司山西分公司违法违规案\n3. 处罚决定书文号:晋银保
*> > 监罚决字〔2022〕31号\n4. 主要违法违规事实或案由:\n\n\n| 序号 | 违法违规事实 |\n| --- | --- |\n| 1 | 编制虚假
*> > 财务资料 |\n| 2 | 给予投保人、被保险人保险合同约定以外其他利益 |\n| 3 | 欺骗投保人、被保险人 |\n\n5. 处罚依据
*> > :\n\n\n\t* 《中华人民共和国保险法》第八十六条\n\t* 《中华人民共和国保险法》第一百一十六条\n\t* 《中华人民共和
*> > 国保险法》第一百六十一条\n6. 处罚决定:\n\n\n\t* 对合众人寿山西分公司责令改正,处15万元罚款\n\t* 对合众人寿山
*> > 西分公司责令改正,处5万元罚款\n\t* 对合众人寿山西分公司责令改正,处5万元罚款\n7. 作出处罚决定的机关名称:中国
*> > 银保监会山西监管局\n8. 作出处罚决定的日期:2022年6月8日\n9. 被处罚个人姓名:无\n10. 被处罚单位名称:合众人寿保
*> > 险股份有限公司山西分公司\n11. 被处罚单位的法定代表人或主要负责人姓名:郑江龙\n12. 发布时间:2022年6月8日\n13.
*> > 罚款金额:25万元
*>         is_truncated = false
*>         need_clear_history = false
*>         usage:prompt_tokens = 1549
*>         usage:completion_tokens = 370
*>         usage:total_tokens = 1919

循环处理所有文件:

use usedocdf, clear 
gen str2000 result = ""
forval i = 1/`=_N' {
 insheetjson result using json/`=document[`i']'.json, column(result) replace offset(`=`i'-1') 
}

format result %10s

再从 result 中提取我们想要的字段即可:

*- 处理结果
gen 年份 = ustrregexs(1) if ustrregexm(result, "1\. 年份:(.*)\\n2\."
gen 标题 = ustrregexs(1) if ustrregexm(result, "2\. 标题:(.*)\\n3\."
gen 处罚决定书文号 = ustrregexs(1) if ustrregexm(result, "3\. 处罚决定书文号:(.*)\\n4\."
gen 主要违法违规事实或案由 = ustrregexs(1) if ustrregexm(result, "4\. 主要违法违规事实或案由:(.*)\\n5\."
gen 处罚依据 = ustrregexs(1) if ustrregexm(result, "5\. 处罚依据:(.*)\\n6\."
gen 处罚决定 = ustrregexs(1) if ustrregexm(result, "6\. 处罚决定:(.*)\\n7\."
gen 作出处罚决定的机关名称 = ustrregexs(1) if ustrregexm(result, "7\. 作出处罚决定的机关名称:(.*)\\n8\."
gen 作出处罚决定的日期 = ustrregexs(1) if ustrregexm(result, "8\. 作出处罚决定的日期:(.*)\\n9\."
gen 被处罚个人姓名 = ustrregexs(1) if ustrregexm(result, "9\. 被处罚个人姓名:(.*)\\n10\."
gen 被处罚单位名称 = ustrregexs(1) if ustrregexm(result, "10\. 被处罚单位名称:(.*)\\n11\."
gen 被处罚单位的法定代表人或主要负责人姓名 = ustrregexs(1) if ustrregexm(result, "11\. 被处罚单位的法定代表人或主要负责人姓名:(.*)\\n12\."
gen 发布时间 = ustrregexs(1) if ustrregexm(result, "12\. 发布时间:(.*)\\n13\."
gen 罚款金额 = ustrregexs(1) if ustrregexm(result, "13\. 罚款金额:(.*)"

compress 
foreach i of varlist _all {
 cap format `i' %10s 
 cap replace `i' = subinstr(`i'"\n""", .)
 cap replace `i' = subinstr(`i'"\t""", .)
}

drop result length 

replace 年份 = subinstr(年份, "年""", .)
destring 年份, replace
save "处理结果"replace 

看起来效果还挺不错!

应用这个技术就可以快速处理文本数据并提取关键变量了!不过缺点就是只能一次处理 2000 字内的文本。或许对于长文本可以通过预先切分、分多次提取。

直播信息

为了让大家更好的理解上面的内容,欢迎各位培训班会员参加明晚 8 点的直播课 「Stata:调用百度文心千帆大模型进行文本分析与文本主要内容提取」

  1. 直播地址:腾讯会议(需要报名 RStata 培训班参加)
  2. 讲义材料:需要报名 RStata 培训班,详情可阅读:一起来学习 R 语言和 Stata 啦!学习过程中遇到的问题也可以随时提问!

更多关于 RStata 会员的更多信息可添加微信号 r_stata 咨询:

附件下载(点击文末的阅读原文即可跳转):

https://rstata.duanshu.com/#/brief/course/6aac8b39269d431eaa9017b25a966463


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存