前言
前面實踐篇基於DeepDive實現從股權交易公告獲取企業與企業之間存在交易關係的概率–實踐篇 已經大概地走了一波如何使用deepdive去做實體抽取,關係概率計算等操作,接下將基於deepdive的數據處理、實體抽取、特徵抽取,結合示例相關文件進行解析。
文章目錄
1.數據處理腳本解析
前面我們第一步就是把股權交易公告數據通過執行 deepdive do articles
實現公告數據導入。
articles
是deepdive的目標名,對應文件app.ddlog
具體導入數據表結構定義如下
@source
articles(
@key
@distributed_by
id text,
@searchable
content text
).
當前articles對象包含兩個字段,分別是id
和content
實現數據導入的規則是在app.ddlog
中articles
的命名與當前目錄的input
文件夾下需要導入的csv文件名對應
2. 數據標註腳本解析
通過執行 deepdive do sentences
實現公告數據標註,具體使用standfordnlp自然語言處理包。
2.1 數據標註結構定義
deepdive默認用standford nlp進行文本處理,可以返回句子的分詞、lemma、pos、NER,具體看一下自然語言處理基礎。
字段名 | 字段解釋 |
---|---|
doc_id | doc_id表示的是articles表中公告對應的id |
sentence_index | sentence_index表示的是公司所在的句子在文章中對應的索引 |
tokens | tokens的結構如下:1: “證券”,其中1是分詞的索引,“證券”是分詞的內容 |
lemmas | lemmas與pos_tag、ner_tags、dep_types和tokens的結構是一樣的,表示詞元 |
pos_tags | pos_tags表示的是句子的詞性 |
ner_tags | ner_tags表示的是實體類型的識別,如果是公司則表示爲“ORG” |
doc_offsets | doc_offsets表示的是每個分詞在文章中的開始位置的索引 |
dep_types | dep_types表示的是每個分詞的句法結構 |
@source
sentences(
@key
@distributed_by
doc_id text,
@key
sentence_index int,
@searchable
sentence_text text,
tokens text[],
lemmas text[],
pos_tags text[],
ner_tags text[],
doc_offsets int[],
dep_types text[],
dep_tokens int[]
).
2.2 數據標註函數實現
這裏定義函數名爲
nlp_markup
的函數,定義入參、返回值以及具體執行程序。over後面定義接入參
doc_id
和context
,然後將返回數據rows數據結構對應定義的sentences
的行數據,最後一句說明了我們這個程序文件是udf/nlp_markup.sh
,輸入是tsv的一行。
function nlp_markup over (
doc_id text,
content text
) returns rows like sentences
implementation "udf/nlp_markup.sh" handles tsv lines.
udf/nlp_markup.sh
文件就是具體實現標註的程序,一般來說我們是可以不動它
set -euo pipefail
cd "$(dirname "$0")"
: ${BAZAAR_HOME:=$PWD/bazaar}
# 啓動standordnlp 自然語言處理執行程序 具體在 udf/bazaar/parser/target/start
# 啓動程序由 sbt/sbt stage編譯得
[[ -x "$BAZAAR_HOME"/parser/target/start ]] || {
echo "No Bazaar/Parser set up at: $BAZAAR_HOME/parser"
exit 2
} >&2
[[ $# -gt 0 ]] ||
# default column order of input TSV
set -- doc_id content
# 將數據tvs行數據轉json
# start Bazaar/Parser to emit sentences TSV
tsv2json "$@" |
# 解析json數據,實現標註
"$BAZAAR_HOME"/parser/run.sh -i json -k doc_id -v content
定義完函數跟執行程序,就需要實現函數調用
sentences += nlp_markup(doc_id, content) :-
articles(doc_id, content).
上面的
+=
其實和其他語言差不多,就是對於來源是articles中的每一行的doc_id
和content
我們都調用nlp_markup
然後結果添加到sentences表中。
這一部分的函數基本都不需要進行改動,都是套路,沿着走就是了。
3.候選實體生成
首先我們再次明確的我們的目標,我們需要從這波非結構化數據裏面,找到企業並且判斷企業跟企業之間的關係。
我們上面以及完成數據初始化導入以及進行的數據標註,接下來我們根據標註後的數據,按照我們的規則,抽取出這段公告裏面的實體也就是企業
3.1 抽取候選實體結構定義
老套路,定義實體表
@extraction
company_mention(
@key
# id
mention_id text,
@searchable
# 企業名稱
mention_text text,
@distributed_by
@references(relation="sentences", column="doc_id", alias="appears_in")
# 對應sentences表的id
doc_id text,
# 對應sentences表的sentence_index
@references(relation="sentences", column="doc_id", alias="appears_in")
sentence_index int,
# 句中開始位置
begin_index int,
# 句中結束位置
end_index int
).
3.2 抽取實體函數實現
這裏的定義就是函數入參、返回值以及程序。
function map_company_mention over (
doc_id text,
sentence_index int,
tokens text[],
ner_tags text[]
) returns rows like company_mention
implementation "udf/map_company_mention.py" handles tsv lines.
udf/map_company_mention.py
就是核心抽取實體代碼,具體邏輯就是根據 sentences裏面的ner_tags,實體類型的識別,抽取類型爲ORG
的數據進行轉換存儲到company_mention裏面,核心邏輯如下:
4. 候選實體關係抽取
前面說了,我們要判斷企業跟企業之間的是否存在交易關係的概率,首先我們得建立企業與企業的集合,基於笛卡兒積。
假如有3個企業,笛卡兒積之後就會出現這樣的結果:
企業1 | 企業2 |
---|---|
阿里 | 騰訊 |
阿里 | 百度 |
騰訊 | 阿里 |
騰訊 | 百度 |
百度 | 阿里 |
百度 | 騰訊 |
4.1 候選實體關係結構定義
@extraction
transaction_candidate(
p1_id text,
p1_name text,
p2_id text,
p2_name text
).
4.2 抽取候選關係函數
由於套路一致,講講核心就行
執行函數:
這裏其實是基於數據庫操作進行笛卡兒積,要求相關聯的兩個候選實體,要是在同一句話裏的,不再同一句話中怎麼知道他們兩個有關係。而且兩個名字不能一樣,識別的時候也有可能一個部分識別了兩個,所以開始位置也不能一樣。
執行程序udf/map_transaction_candidate.py
主要做去重
一般來說,也是不用怎麼改動的。
5.提取特徵邏輯
這就是尋常的機器學習領域了,提取特徵,利用機器學習去分類哪些是有交易關係的。
對於自然語言來說,他的特徵就是上下文,就是上下文
5.1 定義特徵表
@extraction
transaction_feature(
@key
@references(relation="has_transaction", column="p1_id", alias="has_transaction")
p1_id text,
@key
@references(relation="has_transaction", column="p1_id", alias="has_transaction")
p2_id text,
@key
feature text
).
5.2 提取特徵實現
function extract_transaction_features over (
p1_id text,
p2_id text,
p1_begin_index int,
p1_end_index int,
p2_begin_index int,
p2_end_index int,
doc_id text,
sent_index int,
tokens text[],
lemmas text[],
pos_tags text[],
ner_tags text[],
dep_types text[],
dep_tokens int[]
) returns rows like transaction_feature
implementation "udf/extract_transaction_features.py" handles tsv lines.
transaction_feature += extract_transaction_features(
p1_id, p2_id, p1_begin_index, p1_end_index, p2_begin_index, p2_end_index,
doc_id, sent_index, tokens, lemmas, pos_tags, ner_tags, dep_types, dep_tokens
) :-
company_mention(p1_id, _, doc_id, sent_index, p1_begin_index, p1_end_index),
company_mention(p2_id, _, doc_id, sent_index, p2_begin_index, p2_end_index),
sentences(doc_id, sent_index, _, tokens, lemmas, pos_tags, ner_tags, _, dep_types, dep_tokens).
核心腳本 udf/extract_transaction_features.py
,調用了ddlib庫
的get_generic_features_relation
產生了特徵.
以其中一句爲例子,1201734457_3
SELECT sentence_text FROM sentences WHERE doc_id = '1201734457' AND sentence_index =3
公司擬將全資子公司山丹縣芋興粉業 有限責任公司(以下簡稱:芋興粉業)100%股權、全資子公司甘肅天潤 薯業有限責任公司(以下簡稱:天潤薯業)100%股權及甘肅天潤薯業有 限責任公司的全資子公司甘肅大有農業科技有限公司(以下簡稱:大有 科技)100%股權同價轉讓給甘肅亞盛薯業有限責任公司(以下簡稱:亞 盛薯業)。
根據我們觀察 實體是甘肅大有農業科技有限公司以及甘肅天潤薯業有限責任公司,他們進行了股權轉讓
甘肅大有農業科技有限公司實體id:1201734457_3_46_51
甘肅天潤薯業有限責任公司實體id: 1201734457_3_22_27
select feature from transaction_feature where p1_id = '1201734457_3_46_51' and p2_id='1201734457_3_22_27'
總共抽取了79個特徵
具體解析參考:https://blog.csdn.net/weixin_42001089/article/details/90749577
6. 樣本打標
做過機器學習的朋友都知道,訓練模型需要訓練大量的標籤數據進行模型迭代優化,這裏示例的邏輯是導入先驗數據,按照我們預先設定的規則,進行標記,產生一堆正例樣本和反例樣本。
6.1 導入先驗數據
導入已知有交易關係的先驗數據,先驗數據結構如下:
transaction_dbdata(
@key
company1_name text,
@key
company2_name text
).
6.2 初始化標籤表並對結合先驗數據進行打標
定義標籤表 transaction_label
@extraction
transaction_label(
@key
@references(relation="has_transaction", column="p1_id", alias="has_transaction")
p1_id text,
@key
@references(relation="has_transaction", column="p2_id", alias="has_transaction")
p2_id text,
@navigable
label int,
@navigable
rule_id text
).
先驗數據打標規則: 先驗數據與抽選集合企業名稱一致的權重爲3,就是正相關比較大
6.3 對候選數據進行打標
核心腳本udf/supervise_transaction.py
:
這裏基本就是按照定好的邏輯規則進行標註
通過sum計算打標結果總計獲得最終標記transaction_resolve
SELECT r0.p1_id AS column_0,
r0.p2_id AS column_1,
sum(r0.label) AS column_2
FROM transaction_label r0
GROUP BY r0.p1_id, r0.p2_id
最終標註結果如下:
7 變量表定義
首先定義最終存儲的表格,[?]表示此表是用戶模式下的變量表,即需要推到關係的表。這裏我們需要推到的是企業和企業間是否存在交易關係。
has_transaction(p1_id, p2_id) = if l > 0 then TRUE
else if l < 0 then FALSE
else NULL end :- transaction_label_resolved(p1_id, p2_id, l).
8 構建因子圖
8.1 因子圖解釋
Factor Graph 是概率圖的一種,概率圖有很多種,最常見的就是Bayesian Network (貝葉斯網絡)和Markov Random Fields(馬爾可夫隨機場)。
在概率圖中,求某個變量的邊緣分佈是常見的問題。這問題有很多求解方法,其中之一就是可以把Bayesian Network和Markov Random Fields 轉換成Facor Graph,然後用sum-product算法求解。基於Factor Graph可以用sum-product算法可以高效的求各個變量的邊緣分佈。
將一個具有多變量的全局函數因子分解,得到幾個局部函數的乘積,以此爲基礎得到的一個雙向圖叫做因子圖。
所謂factor graph(因子圖),就是對函數因子分解的表示圖,一般內含兩種節點,變量節點和函數節點。我們知道,一個全局函數能夠分解爲多個局部函數的積,因式分解就行了,這些局部函數和對應的變量就能體現在因子圖上。
在概率論及其應用中, 因子圖是一個在貝葉斯推理中得到廣泛應用的模型。
8.2 應用因子圖獲取交易概率
將每一對 has_transaction 中的實體對和特徵表連接起來,通過特徵因子連接,全局學習這些特徵的權重 f
@weight(f)
has_transaction(p1_id, p2_id) :-
transaction_candidate(p1_id, _, p2_id, _),
transaction_feature(p1_id, p2_id, f).
# Inference rule: Symmetry
@weight(3.0)
has_transaction(p1_id, p2_id) => has_transaction(p2_id, p1_id) :-
transaction_candidate(p1_id, _, p2_id, _).
獲取交易概率如下: