LLMGraphTransformerとNeo4jで請求項を眺めてみる
- NISHIO KEI
- 4月17日
- 読了時間: 9分
1.はじめに
・知識グラフ+LLMが良さそうということで、LangChainの中に何かいいものがないかと探したところ、LLMGraphTransformerを発見。
・試してみます。
2.処理の流れ
・設定はこんな感じで。速度と料金的にgemini-2.0を選択
import os
from langchain_experimental.graph_transformers import LLMGraphTransformer
from langchain_openai import ChatOpenAI
from langchain_google_genai import ChatGoogleGenerativeAI
os.environ["GOOGLE_API_KEY"] = GOOGLE_API_KEY
llm = ChatGoogleGenerativeAI(
model="gemini-2.0-flash",
temperature=0,
max_tokens=None,
timeout=None,
max_retries=2,
# other params...
)
llm_transformer = LLMGraphTransformer(llm=llm)
・Neo4jで無料プランでもinstanceを作っておいて
import os
from langchain_neo4j import Neo4jGraph
os.environ["NEO4J_URI"] = "neo4j+s://****.databases.neo4j.io"
os.environ["NEO4J_USERNAME"] = "****"
os.environ["NEO4J_PASSWORD"] = "****"
graph = Neo4jGraph(refresh_schema=False)
・まずは抽出してみる。
#@title tex
tex = """
【特許請求の範囲】
【請求項1】
ユーザの顔及び話し方の音声特徴を取得するための撮影アプリケーションであって、
前記撮影アプリケーションは、
少なくとも前記ユーザの顔の向き、表情、撮影時間、セリフ読み有無を含む第1の指定形式による動画撮影を行う動画撮影機能と、
少なくとも前記ユーザの顔の向き、表情、文章数、ポーズ有無を含む第2の指定形式によるセリフ読み動画撮影を行うセリフ読み動画撮影機能と、を有することを特徴とする撮影アプリケーション。
【請求項2】
前記第1の指定形式は、前記顔の向きが正面又は上下左右であり、前記表情が無表情であり、
前記動画撮影機能は、前記ユーザの顔の向き、表情、撮影時間、セリフ有無を組合せた条件指定により動画撮影を行うことを特徴とする請求項1記載の撮影アプリケーション。
【請求項3】
前記第2の指定形式は、前記顔の向きが正面、上下、及び左右であり、前記表情が自然、怒り、悲しい、嬉しい、嫌悪、恐怖、驚きであり、
前記セリフ読み動画撮影機能は、前記ユーザの顔の向き、表情、文章数、ポーズ有無を組合せた条件指定によりセリフ読み動画撮影を行うことを特徴とする請求項1又は2記載の撮影アプリケーション。
【請求項4】
請求項1記載の撮影アプリケーションをインストールしたことを特徴とするカメラ付き携帯通信端末。
【請求項5】
請求項4記載のカメラ付き携帯通信端末と、WEBRTC(Web Real-time Communication)サーバと、前記WEBRTCサーバの後段に設置された機械学習サーバとが通信ネットワークを介して接続された通信ネットワークシステムにおいて、
前記WEBRTCサーバは、前記カメラ付き携帯通信端末にインストールされた撮影アプリケーションとリアルタイムコミュニケーションを行い、前記動画撮影機能又は前記セリフ読み動画撮影機能によって取得された少なくとも前記ユーザの顔の向き、表情、撮影時間、セリフ読み有無を含む第1の指定形式又は少なくとも前記ユーザの顔の向き、表情、文章数、ポーズ有無を含む第2の指定形式の音声データ及び映像データの登録を行うと共に、登録された前記ユーザの音声データの音声認識処理、及び登録された映像データの画像解析処理を行い、
前記機械学習サーバ上で前記ユーザの音声認識処理結果および画像解析結果の学習を行い、学習モデルを保存することを特徴とする通信ネットワークシステム。
【請求項6】
請求項5記載の通信ネットワークシステムにおいて、前記保存した学習データの機械学習モデルによって前記ユーザのバーチャルヒューマン又は動画を生成することを特徴とする動画生成システム。
"""
・入れるのはDocumentにして入れればよく、複数でも大丈夫
from langchain_core.documents import Document
documents = [Document(page_content=tex)]
#documents = [Document(page_content=tex),Document(page_content=tex2),・・・]
graph_documents = llm_transformer.convert_to_graph_documents(documents)
print(f"Nodes:{graph_documents[0].nodes}")
print(f"Relationships:{graph_documents[0].relationships}")
・おお、こんな感じで取ってくれる。
・ノード:
Nodes:[Node(id='撮影アプリケーション', type='Application', properties={}), Node(id='動画撮影機能', type='Function', properties={}), Node(id='セリフ読み動画撮影機能', type='Function', properties={}), Node(id='ユーザの顔', type='Face', properties={}), Node(id='話し方の音声特徴', type='Feature', properties={}), Node(id='顔の向き', type='Orientation', properties={}), Node(id='表情', type='Expression', properties={}), Node(id='撮影時間', type='Time', properties={}), Node(id='セリフ読み有無', type='Condition', properties={}), Node(id='文章数', type='Number', properties={}), Node(id='ポーズ有無', type='Condition', properties={}), Node(id='カメラ付き携帯通信端末', type='Terminal', properties={}), Node(id='Webrtcサーバ', type='Server', properties={}), Node(id='機械学習サーバ', type='Server', properties={}), Node(id='音声データ', type='Data', properties={}), Node(id='映像データ', type='Data', properties={}), Node(id='音声認識処理結果', type='Result', properties={}), Node(id='画像解析結果', type='Result', properties={}), Node(id='学習モデル', type='Model', properties={}), Node(id='通信ネットワークシステム', type='System', properties={}), Node(id='動画生成システム', type='System', properties={}), Node(id='バーチャルヒューマン', type='Human', properties={})]
・関係:
Relationships:[Relationship(source=Node(id='撮影アプリケーション', type='Application', properties={}), target=Node(id='ユーザの顔', type='Face', properties={}), type='FEATURE', properties={}), Relationship(source=Node(id='撮影アプリケーション', type='Application', properties={}), target=Node(id='話し方の音声特徴', type='Feature', properties={}), type='FEATURE', properties={}), Relationship(source=Node(id='撮影アプリケーション', type='Application', properties={}), target=Node(id='動画撮影機能', type='Function', properties={}), type='FUNCTION', properties={}), Relationship(source=Node(id='撮影アプリケーション', type='Application', properties={}), target=Node(id='セリフ読み動画撮影機能', type='Function', properties={}), type='FUNCTION', properties={}), Relationship(source=Node(id='動画撮影機能', type='Function', properties={}), target=Node(id='顔の向き', type='Orientation', properties={}), type='FEATURE', properties={}), Relationship(source=Node(id='動画撮影機能', type='Function', properties={}), target=Node(id='表情', type='Expression', properties={}), type='FEATURE', properties={}), Relationship(source=Node(id='動画撮影機能', type='Function', properties={}), target=Node(id='撮影時間', type='Time', properties={}), type='FEATURE', properties={}), Relationship(source=Node(id='動画撮影機能', type='Function', properties={}), target=Node(id='セリフ読み有無', type='Condition', properties={}), type='FEATURE', properties={}), Relationship(source=Node(id='セリフ読み動画撮影機能', type='Function', properties={}), target=Node(id='顔の向き', type='Orientation', properties={}), type='FEATURE', properties={}), Relationship(source=Node(id='セリフ読み動画撮影機能', type='Function', properties={}), target=Node(id='表情', type='Expression', properties={}), type='FEATURE', properties={}), Relationship(source=Node(id='セリフ読み動画撮影機能', type='Function', properties={}), target=Node(id='文章数', type='Number', properties={}), type='FEATURE', properties={}), Relationship(source=Node(id='セリフ読み動画撮影機能', type='Function', properties={}), target=Node(id='ポーズ有無', type='Condition', properties={}), type='FEATURE', properties={}), Relationship(source=Node(id='カメラ付き携帯通信端末', type='Terminal', properties={}), target=Node(id='撮影アプリケーション', type='Application', properties={}), type='INSTALL', properties={}), Relationship(source=Node(id='通信ネットワークシステム', type='System', properties={}), target=Node(id='カメラ付き携帯通信端末', type='Terminal', properties={}), type='CONNECT', properties={}), Relationship(source=Node(id='通信ネットワークシステム', type='System', properties={}), target=Node(id='Webrtcサーバ', type='Server', properties={}), type='CONNECT', properties={}), Relationship(source=Node(id='通信ネットワークシステム', type='System', properties={}), target=Node(id='機械学習サーバ', type='Server', properties={}), type='CONNECT', properties={}), Relationship(source=Node(id='Webrtcサーバ', type='Server', properties={}), target=Node(id='撮影アプリケーション', type='Application', properties={}), type='COMMUNICATE', properties={}), Relationship(source=Node(id='Webrtcサーバ', type='Server', properties={}), target=Node(id='音声データ', type='Data', properties={}), type='REGISTER', properties={}), Relationship(source=Node(id='Webrtcサーバ', type='Server', properties={}), target=Node(id='映像データ', type='Data', properties={}), type='REGISTER', properties={}), Relationship(source=Node(id='Webrtcサーバ', type='Server', properties={}), target=Node(id='音声データ', type='Data', properties={}), type='RECOGNIZE', properties={}), Relationship(source=Node(id='Webrtcサーバ', type='Server', properties={}), target=Node(id='映像データ', type='Data', properties={}), type='ANALYZE', properties={}), Relationship(source=Node(id='機械学習サーバ', type='Server', properties={}), target=Node(id='音声認識処理結果', type='Result', properties={}), type='LEARN', properties={}), Relationship(source=Node(id='機械学習サーバ', type='Server', properties={}), target=Node(id='画像解析結果', type='Result', properties={}), type='LEARN', properties={}), Relationship(source=Node(id='機械学習サーバ', type='Server', properties={}), target=Node(id='学習モデル', type='Model', properties={}), type='SAVE', properties={}), Relationship(source=Node(id='動画生成システム', type='System', properties={}), target=Node(id='学習モデル', type='Model', properties={}), type='GENERATE', properties={}), Relationship(source=Node(id='動画生成システム', type='System', properties={}), target=Node(id='バーチャルヒューマン', type='Human', properties={}), type='GENERATE', properties={})]
・Neo4jのインスタントに投入!
graph.add_graph_documents(graph_documents, baseEntityLabel=True)
3.可視化したもの
・Neo4jで見てみる。
・全体図

・この辺が請求項1ですね。使う情報も抽出できてました。
【請求項1】
ユーザの顔及び話し方の音声特徴を取得するための撮影アプリケーションであって、
前記撮影アプリケーションは、
少なくとも前記ユーザの顔の向き、表情、撮影時間、セリフ読み有無を含む第1の指定形式による動画撮影を行う動画撮影機能と、
少なくとも前記ユーザの顔の向き、表情、文章数、ポーズ有無を含む第2の指定形式によるセリフ読み動画撮影を行うセリフ読み動画撮影機能と、を有することを特徴とする撮影アプリケーション。

・一方、請求項2以降の情報を無視してしまっているようなので、1Document=1請求項という形式に変更して再実行。
#@title tex
tex = """
【請求項1】
ユーザの顔及び話し方の音声特徴を取得するための撮影アプリケーションであって、
前記撮影アプリケーションは、
少なくとも前記ユーザの顔の向き、表情、撮影時間、セリフ読み有無を含む第1の指定形式による動画撮影を行う動画撮影機能と、
少なくとも前記ユーザの顔の向き、表情、文章数、ポーズ有無を含む第2の指定形式によるセリフ読み動画撮影を行うセリフ読み動画撮影機能と、を有することを特徴とする撮影アプリケーション。
"""
tex2 = """
【請求項2】
前記第1の指定形式は、前記顔の向きが正面又は上下左右であり、前記表情が無表情であり、
前記動画撮影機能は、前記ユーザの顔の向き、表情、撮影時間、セリフ有無を組合せた条件指定により動画撮影を行うことを特徴とする請求項1記載の撮影アプリケーション。
"""
tex3 = """
【請求項3】
前記第2の指定形式は、前記顔の向きが正面、上下、及び左右であり、前記表情が自然、怒り、悲しい、嬉しい、嫌悪、恐怖、驚きであり、
前記セリフ読み動画撮影機能は、前記ユーザの顔の向き、表情、文章数、ポーズ有無を組合せた条件指定によりセリフ読み動画撮影を行うことを特徴とする請求項1又は2記載の撮影アプリケーション。
"""
tex4 = """
【請求項4】
請求項1記載の撮影アプリケーションをインストールしたことを特徴とするカメラ付き携帯通信端末。
"""
tex5 = """
【請求項5】
請求項4記載のカメラ付き携帯通信端末と、WEBRTC(Web Real-time Communication)サーバと、前記WEBRTCサーバの後段に設置された機械学習サーバとが通信ネットワークを介して接続された通信ネットワークシステムにおいて、
前記WEBRTCサーバは、前記カメラ付き携帯通信端末にインストールされた撮影アプリケーションとリアルタイムコミュニケーションを行い、前記動画撮影機能又は前記セリフ読み動画撮影機能によって取得された少なくとも前記ユーザの顔の向き、表情、撮影時間、セリフ読み有無を含む第1の指定形式又は少なくとも前記ユーザの顔の向き、表情、文章数、ポーズ有無を含む第2の指定形式の音声データ及び映像データの登録を行うと共に、登録された前記ユーザの音声データの音声認識処理、及び登録された映像データの画像解析処理を行い、
前記機械学習サーバ上で前記ユーザの音声認識処理結果および画像解析結果の学習を行い、学習モデルを保存することを特徴とする通信ネットワークシステム。
"""
tex6 = """
【請求項6】
請求項5記載の通信ネットワークシステムにおいて、前記保存した学習データの機械学習モデルによって前記ユーザのバーチャルヒューマン又は動画を生成することを特徴とする動画生成システム。
"""
・さっきよりも色々出してくれましたね。相変わらず請求項2、3の限定はスルー(表現されてない)ですが、請求項4、5はちゃんと出してくれてるようです。
from langchain_core.documents import Document
documents = [Document(page_content=tex),Document(page_content=tex2),Document(page_content=tex3),Document(page_content=tex4),Document(page_content=tex5),Document(page_content=tex6)]
graph_documents = llm_transformer.convert_to_graph_documents(documents)
print(f"Nodes:{graph_documents[0].nodes}")
print(f"Relationships:{graph_documents[0].relationships}")

・抽出するノードの種類・属性や関係性を指定もできるので、このあたりを特許文章抽出用に変形させれば良さそうです。
llm_transformer_props = LLMGraphTransformer(
llm=llm,
#allowed_nodes=["Person", "Country", "Organization"],
#allowed_relationships=["NATIONALITY", "LOCATED_IN", "WORKED_AT", "SPOUSE"],
#node_properties=["born_year"],
)
graph_documents_props = llm_transformer_props.convert_to_graph_documents(documents)
print(f"Nodes:{graph_documents_props[0].nodes}")
print(f"Relationships:{graph_documents_props[0].relationships}")
・可視化ライブラリも充実しているとことで、今後Neo4jと接続して試してみようと思います。
4.企業単位で抽出してみる
・企業単位で請求項を全部使って可視化してみたもの。なんとなく雰囲気でているかも。
・とある企業の出願50件ほどの請求項1を取ってきて、claim_text_listを作っておいて、
from langchain_core.documents import Document
documents = [Document(page_content=t) for t in claim_text_list]
graph_documents = llm_transformer.convert_to_graph_documents(documents)
print(f"Nodes:{graph_documents[0].nodes}")
print(f"Relationships:{graph_documents[0].relationships}")


5.その他
・発明のカテゴリとの関係で、抽出するノードの種類・属性や関係性を指定すれば、俯瞰ツールとしも良さそうなので、研究してみようと思います。
Comments