PythonとNLPによる業務テキストからの手順・ステップ情報抽出テクニック
はじめに:手順・ステップ情報抽出の重要性と課題
日々の業務において、私たちは多様なテキストデータを取り扱います。例えば、製品のマニュアル、システムの操作ログ、顧客からの問い合わせ履歴、バグ報告書などです。これらのテキストには、特定のタスクを実行するための「手順」や、一連の処理の「ステップ」に関する重要な情報が含まれていることが少なくあります。
これらの手順やステップ情報を効率的に抽出できれば、以下のような様々な応用が可能になります。
- マニュアルから作業手順リストを自動生成する。
- ログデータから一連の処理シーケンスを解析し、ボトルネックや異常を検知する。
- バグ報告から問題を再現するための手順を抽出し、デバッグ作業を効率化する。
- 顧客からの問い合わせから、どのような操作で問題が発生したか手順を特定する。
しかし、これらの情報は必ずしも構造化された形式で記述されているわけではありません。番号付きリスト、箇条書き、あるいは単なる自然文として表現されることが一般的です。単純な正規表現だけでは、表現の揺れや複雑な構造に対応しきれない場面が多々発生します。
本記事では、Pythonと自然言語処理(NLP)の技術を活用し、このような非構造化・半構造化テキストから手順やステップ情報を効率的かつ高精度に抽出するための具体的なテクニックをご紹介します。特に、NLPライブラリであるSpaCyを用いた依存構造解析を中心に解説し、実務で役立つコード例を豊富に提供いたします。
手順・ステップ情報のテキストパターン
テキスト中の手順やステップ情報は、様々なパターンで表現されます。これらのパターンを理解することが、適切な抽出手法を選択する第一歩となります。
- リスト形式:
- 番号付きリスト:
1. まずAを行います。2. 次にBを実行します。
- 箇条書きリスト:
- Cを設定します。* Dを確認します。
- 番号付きリスト:
- 指示表現を含む自然文:
- 命令形:
「システムにログインしてください。」
- 動詞+目的語の構造:
「設定ファイルを編集する。」
「ボタンをクリックする。」
- 特定の助詞・助動詞を伴う表現:
「〜を行う必要があります。」
「〜の手順は以下の通りです。」
- 命令形:
- 時系列を示す接続詞・副詞:
まず
,次に
,その後
,最後に
など。
- 特定のキーワード:
手順
,ステップ
,操作方法
,以下を実施してください
など。
これらのパターンは単独で現れるだけでなく、組み合わさって使用されることもあります。例えば、「1. まず、コントロールパネルを開いてください。」のように、リスト形式と指示表現が同時に使われるケースです。
NLPによる手順・ステップ抽出の基本アプローチ
単純なキーワードや正規表現だけでは、同じ単語でも文脈によって意味が異なる場合や、表現の揺れに対応できません。ここでNLPが役立ちます。特に以下の要素を活用します。
- 文分割 (Sentence Splitting): 手順はしばしば複数の文にまたがるため、正確な文分割は重要です。
- トークン化 (Tokenization): テキストを単語や記号などの最小単位(トークン)に分割します。
- 品詞タグ付け (Part-of-Speech Tagging): 各トークンが動詞、名詞、助詞などのどの品詞に当たるかを識別します。指示や操作を表す動詞を特定するために不可欠です。
- 依存構造解析 (Dependency Parsing): 文中の単語間の文法的な関係(主語-述語、動詞-目的語など)を解析します。これにより、「誰/何が」「何を」「どうする」といった構造を捉え、操作の対象(目的語)や実行されるアクション(動詞)を精度高く特定できます。
これらの情報を組み合わせることで、「〜をする」という指示や操作を表現している部分を、その操作の対象も含めて構造的に抽出することが可能になります。
SpaCyを活用した手順・ステップ抽出の実践
ここでは、強力なオープンソースNLPライブラリであるSpaCyを用いて、具体的な手順・ステップ情報の抽出方法を見ていきます。SpaCyは高速で高精度なNLP機能を提供しており、実務での利用に適しています。
まず、SpaCyをインストールします。
pip install spacy
python -m spacy download ja_core_news_sm # 日本語モデルのダウンロード
基本的な指示表現の抽出
動詞とその目的語の組み合わせは、手順や操作を示す最も一般的なパターンの一つです。SpaCyの依存構造解析を利用して、このパターンを抽出します。
例えば、「設定ファイルを編集する」という文では、「編集する」が動詞、「設定ファイル」がその目的語です。依存構造解析の結果では、「設定ファイル」は「編集する」に対する obj
(目的語) の関係を持ちます。
import spacy
# 日本語モデルをロード
nlp = spacy.load("ja_core_news_sm")
text = "以下の手順でシステムをアップデートしてください。まず、設定ファイルを開きます。次に、バージョン情報を確認し、必要な変更を加えます。最後に、システムを再起動します。"
# テキストを処理
doc = nlp(text)
print("--- 依存構造解析結果の例 ---")
for token in doc:
# token.text: テキスト, token.lemma_: 原形, token.pos_: 品詞, token.dep_: 依存関係, token.head.text: 係り先のテキスト
print(f"{token.text}\t{token.lemma_}\t{token.pos_}\t{token.dep_}\t{token.head.text}")
print("--------------------------")
print("\n--- 手順・ステップ候補の抽出 ---")
procedure_steps = []
for sent in doc.sents: # 文ごとに処理
step_parts = []
# 文内のトークンを走査
for token in sent:
# 動詞(VERB)を検出
if token.pos_ == "VERB":
action = token.lemma_ # 原形をアクションとする
target = ""
# その動詞にobj(目的語)として直接かかっている名詞句を探す
for child in token.children:
if child.dep_ == "obj" and child.pos_ == "NOUN":
target = child.text # 目的語のテキストを取得
# 目的語に付属する形容詞や名詞なども含める場合は、child.subtreeを探索するなど拡張が必要
break # シンプルな例として最初の目的語を採用
if action:
# 必要に応じて、アクションと対象を結合したり、特定の条件でフィルタリング
step_description = f"{action}"
if target:
step_description += f" ({target})"
# 副詞句などを前置することも検討
# 例: まず (action)
# 時系列を示す語を検出してステップに追加することも可能
temporal_modifier = ""
for ancestor in token.ancestors:
if ancestor.text in ["まず", "次に", "最後に"]:
temporal_modifier = ancestor.text
break
if temporal_modifier:
step_description = f"{temporal_modifier}、{step_description}"
step_parts.append(step_description)
# 抽出されたパーツを結合してステップとしてリストに追加(シンプルな例)
if step_parts:
procedure_steps.append(" ".join(step_parts)) # 文全体をステップとして扱うなど、結合方法は要検討
# 結果の表示
for i, step in enumerate(procedure_steps):
print(f"ステップ {i+1}: {step}")
実行結果の例:
--- 依存構造解析結果の例 ---
以下 以下 NOUN nmod 手順
の の ADP case 以下
手順 手順 NOUN nsubj アップデート
で で ADP case 手順
システム システム NOUN obj アップデート
を を ADP case システム
アップデート アップデート NOUN root アップデート
し する VERB aux アップデート
て て AUX aux アップデート
ください くださる VERB acl アップデート
。 。 PUNCT punct アップデート
まず まず ADV advmod 開く
、 、 PUNCT punct 開く
設定 設定 NOUN compound ファイル
ファイル ファイル NOUN obj 開く
を を ADP case ファイル
開きます 開く VERB root 開きます
。 。 PUNCT punct 開きます
次に 次に ADV advmod 確認
、 、 PUNCT punct 確認
バージョン バージョン NOUN compound 情報
情報 情報 NOUN obj 確認
を を ADP case 情報
確認 確認 NOUN root 確認
し する AUX aux 確認
、 、 CCONJ cc 加える
必要 必要 ADJ root 必要
な だ AUX cop 必要
変更 変更 NOUN obj 加える
を を ADP case 変更
加え 加える VERB conj 確認
ます ます AUX aux 加え
。 。 PUNCT punct 確認
最後に 最後に ADV advmod 再起動
、 、 PUNCT punct 再起動
システム システム NOUN obj 再起動
を を ADP case システム
再起動 再起動 NOUN root 再起動
し する AUX aux 再起動
ます ます AUX aux 再起動
。 。 PUNCT punct 再起動
--------------------------
--- 手順・ステップ候補の抽出 ---
ステップ 1: アップデート (システム)
ステップ 2: まず、開く (ファイル)
ステップ 3: 次に、確認 (情報) 加える (変更)
ステップ 4: 最後に、再起動 (システム)
このコードでは、各文を処理し、動詞を見つけたらその目的語 (dep_ == "obj"
) を探しています。また、時系列を示す副詞(まず
, 次に
, 最後に
)があれば、それも一緒に抽出しています。
リスト形式の手順抽出
番号付きリストや箇条書きは、正規表現と組み合わせることで比較的容易に抽出できます。抽出した各項目に対して、上記のようなNLP処理を適用することで、より詳細な操作内容を構造化できます。
import re
import spacy
nlp = spacy.load("ja_core_news_sm")
text_list = """
作業手順:
1. システムにログインします。
2. 設定画面を開いてください。
3. ユーザー情報を入力し、保存ボタンをクリックします。
4. ログアウトします。
"""
# リスト形式の項目を正規表現で抽出
list_items = re.findall(r'^\s*[\d\-\*]\s*(.+)', text_list, re.MULTILINE)
print("\n--- リスト項目ごとのNLP処理 ---")
processed_steps = []
for i, item in enumerate(list_items):
doc_item = nlp(item)
step_description = item # 元のテキストを保持
actions = []
for token in doc_item:
if token.pos_ == "VERB":
action = token.lemma_
target = ""
for child in token.children:
# 目的語やそれに類する依存関係(例: obj, obl, iobjなど、モデルによる)を探す
if child.dep_ in ["obj", "obl", "iobj"] and child.pos_ in ["NOUN", "PROPN"]:
target = child.text
# より広範な名詞句を取得する場合:
# target = " ".join([t.text for t in child.subtree])
break
action_desc = action
if target:
action_desc += f" ({target})"
actions.append(action_desc)
# 抽出したアクション情報を整形してリストに追加
if actions:
processed_steps.append(f"ステップ {i+1}: {item} -> アクション: {', '.join(actions)}")
else:
processed_steps.append(f"ステップ {i+1}: {item} -> アクション候補なし")
# 結果の表示
for step in processed_steps:
print(step)
実行結果の例:
--- リスト項目ごとのNLP処理 ---
ステップ 1: システムにログインします。 -> アクション: ログイン (システム)
ステップ 2: 設定画面を開いてください。 -> アクション: 開く (画面)
ステップ 3: ユーザー情報を入力し、保存ボタンをクリックします。 -> アクション: 入力 (情報), クリック (ボタン)
ステップ 4: ログアウトします。 -> アクション: ログアウト
この例では、正規表現でリスト項目を抽出し、それぞれの項目に対してNLP処理を行っています。抽出される依存関係 (dep_
) は使用するSpaCyモデルによって若干異なる場合があるため、obj
だけでなく obl
や iobj
など、目的語に相当する可能性のある関係も考慮すると、より網羅的になることがあります。
より複雑な表現への対応とカスタム化
実際の業務テキストでは、単純な「動詞 + 目的語」だけでなく、様々な修飾語やオプション情報が付随します。例えば、「必ず管理権限でシステムにログインします。」や「設定ファイルをエディタで編集する。」などです。
これらの情報を抽出するには、依存構造ツリーをより深く解析する必要があります。
- 修飾語の抽出: 動詞や目的語にかかる副詞(
advmod
)、形容詞(amod
)、前置詞句(obl
,nmod
など)を依存関係からたどります。 - 連結された操作: 「Aして、Bする」のように、複数の操作が接続詞(
cc
,conj
)で連結されている場合、それぞれの操作を独立して抽出する必要があります。 - オプション情報: かっこ書きや特定の記号で囲まれたオプション情報は、別途正規表現などで抽出し、関連するステップに紐づける工夫が必要です。
SpaCyのtoken.children
や token.ancestors
、token.subtree
などのプロパティを活用することで、これらの関連情報を構造的に捉えることができます。特定の業務ドメインに特化した表現が多い場合は、カスタムの依存関係パターンを定義してマッチングを行う「ルールベースマッチング」や、固有表現認識(NER)の機能を活用して操作対象の種類(例: ファイル名、ユーザー名、ボタン名)を識別することも有効です。
応用事例
本記事で解説した手法は、以下のような実務課題に応用できます。
- マニュアル・ドキュメントからの手順自動生成: 既存のドキュメントから手順部分を抽出し、作業指示書やチェックリストの自動生成システムに組み込む。
- ログ解析の高度化: システムログ中の処理シーケンスを自動で抽出し、障害発生時の原因特定や予兆検知に役立てる。特定の操作が期待通りの順序で実行されたかを確認する。
- 問い合わせ対応支援: 顧客からの報告メールやチャットから、問題発生時の操作手順を自動抽出し、サポート担当者が状況を素早く把握できるようにする。
- テストケース自動生成: 仕様書や要件定義書から操作手順を抽出し、自動テストのシナリオ生成に利用する。
実務での考慮事項とパフォーマンス
本手法を実務システムに組み込む際には、いくつかの考慮事項があります。
- 表現の多様性への対応: 自然言語の表現は非常に多様です。単純な依存構造パターンだけでは網羅しきれない場合があります。ドメイン固有の表現や同義語に対応するために、カスタム辞書や、より洗練されたパターン定義、あるいは機械学習を用いたアプローチとの組み合わせを検討する必要があります。
- 抽出精度と網羅率: どのような精度で、どれだけ多くの手順を抽出したいかによって、アプローチを調整する必要があります。高精度を目指すなら厳密なパターンマッチング、網羅率を重視するならパターンを緩和するなどです。
- パフォーマンス: 大量のテキストデータを処理する場合、SpaCyによる解析はCPUリソースを消費します。バッチ処理化、並列処理、または軽量なモデル(
ja_core_news_sm
など)の選択、GPUの活用などを検討し、処理速度を確保する必要があります。特に、SpaCyのパイプライン処理は効率的ですが、不要なコンポーネント(例: NER, parser)を無効化することで高速化できます。 - 曖昧性の解決: 同じ表現でも文脈によって手順ではない場合があります。文全体や周囲の情報を利用して、本当に手順として意図されているかを判断する仕組みが必要になることがあります。これはルールベースだけでは難しく、より高度な文脈理解が必要になります。
- システム設計: 抽出処理をマイクロサービスとして切り出す、抽出ルールや辞書を外部設定ファイルで管理するなど、保守性・拡張性の高い設計を心がけることが重要です。
まとめ
本記事では、PythonとSpaCyを活用した、業務テキストからの手順・ステップ情報抽出テクニックについて解説しました。単純な正規表現では対応が難しい自然文中の指示表現や、リスト形式の手順を、品詞タグ付けや依存構造解析を用いて構造的に捉える具体的な手法とコード例をご紹介しました。
これらのテクニックは、マニュアルの解析、ログからの処理シーケンス特定、問い合わせ内容からの手順抽出など、様々な実務課題に応用可能です。実際のテキストデータは多様な表現を含むため、本記事で紹介した基本アプローチをベースに、正規表現、カスタム辞書、より複雑な依存関係パターンの定義などを組み合わせることで、抽出精度や網羅率を向上させることができます。
自然言語処理は強力なツールですが、万能ではありません。処理対象となるテキストデータの特性をよく理解し、課題に対して最も効果的な手法を選択・組み合わせることが、実務で成果を出すための鍵となります。本記事が、皆様のテキストからの情報抽出の実践に役立てば幸いです。