web本文抽出 using crf
DESCRIPTION
TRANSCRIPT
Web本文抽出 using CRF
2010/7/4
中谷 秀洋@サイボウズ・ラボ
本文抽出 for Project Gutenberg
• Project Gutenberg (http://gutenberg.org/)
– 著作権フリーテキストのデータベース
• 英語が主だが、最近は他の言語もぼちぼち
– コーパスの宝庫
• DVDのisoイメージがtorrentで配布されている
• テキストの前後にヘッダ・フッタが
– 分量多め&規則性無し。カオス!
– サボると頻出語の上位に“Gutenberg”
パターン1
ここから本文
それっぽいセパレータ
パターン2
ここから本文
それっぽいセパレータ
パターン3
それっぽいセパレータ
本当はここから本文
ここから本文かと思いきや
正規表現による切り出し
http://d.hatena.ne.jp/n_shuyo/20081118/gutenberg
本文抽出 for Web
• ExtractContent for Ruby – Webページの本文抽出を行うRubyモジュール
• http://rubyforge.org/projects/extractcontent/
• http://labs.cybozu.co.jp/blog/nakatani/2007/09/web_1.html
– HTML::ExtractContent • Perl への移植&改良 by はてな
• Webページカテゴライズのために開発 – Pathtraq (http://pathtraq.com/)
• みんなのアクセスログで作るランキングサービス
– カテゴライズはナイーブベイズで
– 分類アルゴリズムより 本文抽出の方が精度への影響が大きい
本文抽出の方が 精度への影響が大きい
ノイズの少ない コーパス重要
ExtractContentのアルゴリズム概略
• html をブロックに分割
• ブロックごとにスコアを計算 – 句読点が多い – 非リンクテキストが長い – 本文っぽくないフレーズが含まれている
• 連続するブロックを「大ブロック」にまとめる – スコアの高いものをつなげていく – スコアが低いとつながる確率は減衰していく
• スコアが最大となる「大ブロック」が本文
• 「ヒューリスティック」と言えば聞こえがいいが – 思いつきのアイデア+感覚による調整
ExtractContentのコード(抜粋) module ExtractContent # Default option parameters. @default = { :threshold => 100, :min_length => 80, :decay_factor => 0.73, :continuous_factor => 1.62, :punctuation_weight => 10, :
# スコア算出 c = (notlinked.length + notlinked.scan(punctu.. factor *= decay_factor not_body_rate = block.scan(waste_expressions).. c *= (0.72 ** not_body_rate) if not_body_rate>0 c1 = c * continuous :
ExtractContentの課題
• 本文おまけ問題 – 本文とその他の要素(関連記事リンク、コメント)が
<br> 区切りで続く場合に、それらを分離できない • 現行の方式で <br> でも区切ると、1つのブロック長が短く&
本文ブロック数が増えすぎてスコアの評価がうまくいかない
• 本文がない問題 – 「本文がない/極端に短い」ページで、出来るだけ長
いテキスト(≠本文)を抽出してしまう
– サイドバーに長文のレビューや自己紹介が書かれていて、そっちを抜いてしまうケースも。
• アプリごとに違うよ問題 – 「本文」として求める範囲がアプリによって違う
• 分類ではコメント不要。全文検索では必要。
本文がない/短い
ExtractContentのアルゴリズム概略(再)
• html をブロックに分割
• ブロックごとにスコアを計算 – 句読点が多い
– 非リンクテキストが長い ←素性
– 本文っぽくないフレーズが含まれている
• 連続するブロックを「大ブロック」にまとめる – スコアの高いものをつなげていく
– スコアが低いとつながる確率は減衰 ←転移確率
• スコアが最大となる「大ブロック」が【本文】 ←ラベル
どうみても「系列ラベリング」の問題
系列ラベリング
• 系列に対してラベルを付与する
– 観測変数と隠れ変数が1対1に対応
• 様々な問題を解くための定式化の一つ
– 形態素解析
• 品詞推定
• 分かち書き
– 係り受け
– 音声認識
– DNA解析
I am a pen
代名詞 動詞 不定冠詞 名詞
観測変数
隠れ変数
隠れマルコフモデル
– HMM(Hidden Markov Model) – Pettern Recognition and Machine Learning 13章
• 代表的な系列タギング手法の一つ
• 隠れ変数は(1次の)マルコフ連鎖をなす – 観測変数は対応する隠れ変数にのみ依存 – 観測変数間の条件付独立を仮定しない
• 高速な計算方法がある – Baum-Welch/Viterbi
• 教師無し
• 生成モデル
𝑥1 𝑥2 𝑥3 𝑥𝑛
𝑦1 𝑦2 𝑦3 𝑦𝑛
𝑝 𝑦𝑖+1 𝑦𝑖 と𝑝 𝑥𝑖 𝑦𝑖 から 𝑝(𝒙, 𝒚)と𝑝(𝒚|𝒙)を求める
HMM の問題点
• 未知の観測値を扱うことが出来ない – p(X|Y) が値を持たないと計算できない
• 非独立/同時に起きうる素性を扱うことが出来ない – 「テキストが長い」と「句読点が多い」は同時に起きやすい
– 「<table>タグを含む」と「<ul>タグを含む」は非独立
• 大域的な最適性を得られない可能性がある – 分岐の少ない経路が選ばれやすい
• Label bias と Length bias
– 同じラベルを持つ系列の続く確率が指数的に減衰するため、「長い本文」が選ばれにくい • 局所的な条件付き確率の積によって尤度を求めるため
[Kudo+ 2004] より
CRF(条件付き乱数場) » CRF (Conditional Random Fields)[Lafferty+ 2001]
• 系列ラベリングのための識別モデル – 無向グラフ/マルコフ確率場 [PRML 8章] – クリークに対しバイナリ素性 𝑓𝑘(𝑦𝑖−1, 𝑦𝑖 , 𝒙)を定義
• 例:「𝑥𝑖が大文字で始まる」かつ「𝑦𝑖が名詞」なら1
– 𝑝 𝒚 𝒙 ∝ exp 𝜆𝑘𝑓𝑘 𝑦𝑖−1, 𝑦𝑖 , 𝒙𝑘 より𝒚の推定を行う • 高速に計算するアルゴリズムがある(Forward/Backward)
• HMMより最適な系列を得やすい
• Mecabで利用
素性からエネルギー関数を定義、𝑝(𝒚|𝒙)を直接計算する
𝒙
𝑦1 𝑦2 𝑦3 𝑦𝑛 𝑦0 𝑦𝑛+1
Linear-chain CRF
HMM と CRF の相違点
• Vapnikの原理:ある問題を解くとき,その問題よりも難しい問題を途中段階で解いてはならない
• "When solving a problem of interest, do not solve a more general problem as an intermediate step. Try to get the answer that you really need but not a more general one."
• http://en.wikipedia.org/wiki/Transduction_(machine_learning)
Hidden Markov Model Conditional Random Fields
状態空間モデル (有向グラフィカルモデル)
マルコフ確率場 (無向グラフィカルモデル)
生成モデル 識別モデル
教師無し 教師有り
𝑝(𝑥𝑖|𝑦𝑖)で記述できる素性 バイナリ素性を自由に設計
𝑝(𝒙, 𝒚)から𝑝(𝒚|𝒙)を計算 𝑝(𝒚|𝒙)を直接計算
計算量はどちらも O(素性数×状態数^2×系列長) だが、 CRFは素性数が爆発する傾向あり(bigramの素性=状態数^2)
CRFのPython実装 » http://github.com/shuyo/iir/blob/master/sequence/crf.py
• Linear-chain CRF の学習&ラベリング実装 – 1つの素性に1つの観測値
• 簡略化というより 実装開始時の誤解から……
• [Lafferty+ 2001]のグラフが右図だった……
– scipy の BFGS を使ってパラメータを推論 • [Lafferty+ 2001] Forward/Backward • [Sutton+ 2006] gradient • L2正則化
– numpy/scipyにできる限り処理させる • ラベル数=11、素性数=700、学習データの系列長=2500でパ
ラメータ推論に4分 • 系列長=300のラベリングに4秒 • スクリプト言語のわりには?
• 実用より、「読める実装」としての値打ち?? – CRF処理部は 200行弱
アプリケーション
まずは Project Gutenbergで
試してみよう
Project Gutenberg本文抽出 using CRF • 系列ラベリングの問題に定式化
– テキストの空行でパラグラフに分割 – パラグラフの系列に対し、 3種類のラベル H(ヘッダ)、
B(本文)、F(フッタ)を付与する
• 素性設計(素性数: 188) – 特徴的なキーワードが含まれているか
• Project, Gutenberg, David Reed, など
– 使われている文字種 • 大文字、数値、記号
– 先頭、末尾 • “*” が続いている、インデントされている
– 行数 • 3行以上ある、2行以上の空行で区切られている
• 学習データ=7(系列長=3300) • http://github.com/shuyo/iir/blob/master/sequence/pg.py
実行例(パターン3)
• 正しく抽出(先頭が付与されたラベル)
H Project Gutenberg's Etext of Shakespeare's First Folio/35 Plays H Copyright laws are changing all over the world, be sure to check H Please take a look at the important information in this header. : ( 66 paragraphs) H If you find any scanning errors, out and out typos, punctuation H David Reed H Project Gutenberg's Etext of Shakespeare's First Folio/35 Plays B To the Reader. B This Figure, that thou here feest put, It was for gentle Shakesp B B.I. : ( 27143 paragraphs) B Cym. Laud we the Gods, And let our crooked Smoakes climbe to B Exeunt. B FINIS. THE TRAGEDIE OF CYMBELINE. F End of Project Gutenberg's Etext of Shakespeare's First Folio/35
長い系列でも 正しくラベリング
不規則な本文開始位置を 正しく判定
CRFで Webページ本文抽出
Web本文抽出 using CRF
• 系列ラベリングの問題に定式化 – htmlを閉じタグおよび<br>で分割、ブロックの系列を得る – ブロックの系列に9種類のラベル付けを行う
• head, header, menu, title, body, comment, linklist, cm, footer
• 素性設計(647個) – タグ(a/p/div/...) – キーワード(Copyright/会社概要/利用規約/など) – 句読点、日付、アフィリエイトリンク – テキスト長、リンクされているテキスト長
• 学習データ=15(系列長=2500)
• 素性設計以外にヒューリスティックな工夫はしない – script/style タグの除去のみ例外として行っている – Google AdSense Section Targetは無視
• 本文抽出における魔法のアイテム
• http://github.com/shuyo/iir/blob/master/extractcontent/webextract.py
実行例(本文おまけ~Yahoo! Sports)
[head] <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" " [header] <body class="yj950-2"> <div id="wrapper"> <!--- header ---> [header] <div class="yjmth"> <div class="yjmthproplogoarea"><a href=htt [header] <div class="yjmthloginarea"><strong><a href="https://login.yaho : ( 29 blocks) [header] <li title="ニュース" class="active_click"><a href="/news"> [header] <li title="コラム" class="end"><a href="/column/">コラム</ [header] <!---/globalnavi---> <div id="contents-header"> <div id="cat-pa [title] <!--- body ---> <div id="contents-body"> <span class="yj-guid" [title] <em>サンケイスポーツ - 2009/7/9 7:52</em> </div> [body] <!----- article -----> <div class="Article clearfix"> <table [body] <p> (セ・リーグ、巨人3x-2横浜、11 [body] 一塁ベースを回っても、坂本は下を向いたま : ( 5 blocks) [body] 「チームにとっても彼にとっても大きな本塁 [body] 現役時代に何度もスランプを味わった原監督 [body] 【関連記事】<br /> [linklist] ・<a href="http://www.sanspo.com/baseball/news/090709/bsa09070 [linklist] ・<a href="http://www.sanspo.com/baseball/news/090709/bsa09070 [linklist] ・<a href="http://www.sanspo.com/baseball/news/090709/bsa09070 : ( 3 blocks) [linklist] <!-- anemos --> <!-- /anemos --> <div class="Kejiban"> <im
関連記事へのリンクを linklist として正しく分類
実行例(はてなダイアリー)
[head] <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> [header] <body> <div id="simple-header"> <a href="http://www.hatena. : ( 9 paragraphs) [header] <p class="sectionheader"><span class="sectioncategory"><a hre [body] <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Py [body] <p>でも以下のように書くと、<a class="keyword" hr [body] <pre class="syntax-highlight"> <span class="synComment"># A, B, : ( 2 paragraphs) [body] <pre class="syntax-highlight"> labels = ["<span class="syn [body] <p>そのせいで以下のようなハマりパターンも [body] <pre class="syntax-highlight"> <span class="synStatement">def</ [comment] <p class="sectionfooter"><a href="/n_shuyo/20100629/python"> [comment] <!-- google_ad_section_end --> </div> [comment] <form id="comment-form" method="post" action="/n_shuyo/comment : ( 14 paragraphs) [comment] <div class="refererlist"> <div class="caption"> [comment] <div class="refererlist"> <div class="caption"><a [linklist] <ul> <li><a href="http://d.ha [linklist] <div class="refererlist"> <div class="caption : ( 121 paragraphs)
コメントを分離 アプリごとに必要なラベルを
実行例(Yahoo! Japan トップ)
[head] <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" " [menu] <body> <div id="wrapper"> <div id="header"> <div id="masthead" [menu] <ul id="mhicon"> <li id="mhi1st"><a title="Yahoo! BB" href="r/m [menu] <li id="mhi2nd"><a title="オークション" href="r/mauc">オ : ( 30 paragraphs) [menu] <li id="clr5"><a href="r/header/color/5/*-http://www.yahoo.co. [menu] <li id="clr6"><a href="r/header/color/6/*-http://www.yahoo.co. [menu] <p class="help"><a href="r/mht">ヘルプ</a></p></div> </div> < [linklist] <hr class="separate"> <div id="contents"> <div id="toptxt"> <u [linklist] <li id="toptxt2"><a href=s/69879>全国約1000件の花火大莨 [linklist] <li id="toptxt3"><a href=s/69950>新機能は?「ポケモン : ( 150 paragraphs) [linklist] <li><a title="Yahoo!ノートパッド" class="second" href="r/p [linklist] <li><a title="Yahoo!ブリーフケース" class="third" href="r [linklist] <div id="pbindexbg"><div id="pbindex"> <div id="pbcalendar"><di [footer] </tr> <tr> <td><a href="f/pbox/clndr/06/27/*-http://calendar.ya [footer] <td><a href="f/pbox/clndr/06/28/*-http://calendar.yahoo.co.jp/? [footer] <td><a href="f/pbox/clndr/06/29/*-http://calendar.yahoo.co.jp/? : ( 101 paragraphs) [footer] <li><a href="r/fdi">免責事項</a></li></ul> [footer] <address>Copyright (C) 2010 Yahoo Japan Corporation. All Rights [footer] </body> <!--http://ard.yahoo.co.jp/SIG=15blcke9p/M=300330001.
記事見出しや抜粋を linklist として正しく分類
実行例(しょこたんブログ)
[head] <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " [menu] <body id="mainIndex"> <!--bodyTop--> <ul id="amebaBar"> <li id [menu] <li><a href="http://pigg.ameba.jp/" title="ピグ">ピグ</a></ [menu] <li class="last"><a href="http://blog.ameba.jp/ucs/entry/srvent : ( 167 paragraphs) [menu] <p><a href="http://blog.ameba.jp/reader.do?bnm=nakagawa-shoko"> [menu] <!--//.readerMainLink--> <div class="page articlePaging"> <a ti [menu] <!--TopPagingBottom--> <div class="entry new"> <div class="ent [cm] <!--//.entry_head--> <h3 class="title"><!-- google_ad_s [cm] <span class="theme">テーマ:<!-- google_ad_section_s [cm] <DIV>ぽこ(<●><●>)メポぽん</DIV><DIV> : ( 33 paragraphs) [cm] <!--//#footer_ad--></div> [cm] <!--//#sub_main--> </div><!--//#main--> </div> [cm] <!--//#subFirstContentsArea--> </div> [linklist] <!--//#firstContentsArea--> <div id="sub_b"> <!--subBTop--> <!-- [linklist] “中川翔子物語~空色デイズ~”連載中<br [linklist] 原明日美 (著)<br> : ( 53 paragraphs) [linklist] <li><a href="http://ameblo.jp/nakagawa-shoko/theme-10014191488.
手強すなよ……
参考
CRFについて雑感
• CRFはおもしろい! – 非独立な素性を好きに設計できる
• 効果を見込めそうな素性を適当に放り込める
– 不必要な素性を選んでしまっても性能悪化しにくい • 職人技がなくても、それなりのものが作れる
– 期待していたより精度が高い • おもしろいものがいろいろ作れそう!!!
• CRFはまだちょっと難しい – こなれた解説がまだない
• エンジニアにはハードルが高い
– ライブラリはあるが研究用? • アプリからはまだ使いにくい
今回試した範囲での「感想」ですので、あしからず。
CLEANEVAL: 本文抽出コンテスト
» http://cleaneval.sigwac.org.uk/
• 2007年に行われたWebページcleaning (本文抽出)のコンテスト – 英語と中国語の開発用データセット(約60件ずつ)
は現在もサイトで配布されている • テスト用データセットは約650件ずつあるらしいが、
配布されていない?
• あまり変な(=普通な)データはなさそう。 – 開発データセット120件中、「本文なし」は1件だけ
– ファイルサイズの最大は150KB
– 結果: • 中国語は参加1組。精度は18%……
• 英語は9組。精度の最高値は84%
先行研究
• [Marek+ 2007] Web Page Cleaning with Conditional Random Fields – CLEANEVALで精度が一番高かったチーム – 今回紹介したのとだいたい同じ内容?
• 4日前に存在に気づいた。あえてまだ読んでない
– 評価はCLEANEVALのデータセット(英語)に対してのみ • 他の言語でもうまくいくの? • 現実には1MB超えるhtmlとかざらにあるんだけど
• 研究者にそこらへんのモチベーションはない – できることはわかってるけどやってない
• 「2chまとめ系ブログの本文抽出」で論文書けないし
– エンジニアが がんばる!
機械学習をはじめたいエンジニアへ
• 「機械学習 はじめよう」 – gihyo.jpにて大絶賛(?)連載中!
• http://gihyo.jp/dev/serial/01/machine-learning
– 機械学習を知らない人、勉強してみたい人向け • チュートリアルではなく結構硬派に
• 数式ばんばん
– 実際に勉強し始めたらつまずきそうなところ • 独立とか、近似とか
• 事前分布とか、事後分布とか
– 次回第2回は 7/10 ごろ掲載予定
• 機械学習を初めてまだ1年のにわかですが、応援・ご教授いただけると嬉しいです
References
• [Lafferty+ 2001] Conditional Random Fields: Probabilistic Models for Segmenting and Labeling Sequence Data
• [Sutton+ 2006] An Introduction to Conditional Random Fields for Relational Learning
• [Kudo+ 2004] Conditional Random Fieldsを用いた日本語形態素解析
• [Marek+ 2007] Web Page Cleaning with Conditional Random Fields
• 岡野原さんの「機械学習による自然言語処理チュートリアル~PerceptronからCRFまで~」 – http://hillbig.cocolog-nifty.com/do/2008/08/post_040f.html