Neo4jを使用したテキストからのエンティティとリレーション抽出

## はじめに

![napkin-selection (36).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/e2c1fb75-1aae-e78a-583e-b6bcf1dda911.png)

本記事では、自然言語処理（NLP）技術とNeo4jグラフデータベースを組み合わせて、テキストからエンティティとリレーションを抽出し、視覚化する方法を詳しく解説します。日本語テキストを対象とし、Windows環境での実装を前提としています。

## 目次

1. 環境設定
2. エンティティとリレーション抽出の実装
3. Neo4jへのデータ格納
4. 結果の確認とビジュアライゼーション
5. トラブルシューティング
6. 発展的トピックと最適化

## 1. 環境設定

まず、必要なツールとライブラリをインストールします。

```bash
pip install spacy ginza neo4j
python -m spacy download ja_core_news_sm
```

また、[Neo4j公式サイト](https://neo4j.com/download/)からNeo4j Desktopをダウンロードし、インストールしてください。

## 2. エンティティとリレーション抽出の実装

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/c4e821f5-a878-510d-2915-fc2903068e86.png)

以下のPythonスクリプトを使用して、テキストからエンティティとリレーションを抽出します。

```python
import spacy
import ginza

nlp = spacy.load('ja_core_news_sm')

def extract_entities_and_relations(text):
    doc = nlp(text)
    entities = [(ent.text, ent.label_) for ent in doc.ents]
    
    relations = []
    for sent in doc.sents:
        for token in sent:
            if token.dep_ in ["nsubj", "iobj", "dobj"]:
                subject = token.text
                relation = token.head.text
                objects = [child for child in token.head.children if child.dep_ in ["dobj", "iobj"]]
                for obj in objects:
                    relations.append((subject, relation, obj.text))
            
            # 名詞句間の関係を抽出
            if token.pos_ == "NOUN" and token.head.pos_ == "NOUN":
                relations.append((token.text, "関連", token.head.text))

    return entities, relations

# 使用例
text = "山田太郎は東京大学で人工知能を研究しています。彼は機械学習の専門家です。"
entities, relations = extract_entities_and_relations(text)
print("Entities:", entities)
print("Relations:", relations)
```

## 3. Neo4jへのデータ格納

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/ec271a9c-7da0-3886-c6a8-8c1d67c10f43.png)

抽出したエンティティとリレーションをNeo4jに格納します。

```python
from neo4j import GraphDatabase

uri = "bolt://localhost:7687"
user = "neo4j"
password = "your_password"  # Neo4jで設定したパスワードに変更してください

driver = GraphDatabase.driver(uri, auth=(user, password))

def add_entity_and_relation(tx, entities, relations):
    # エンティティの追加
    for entity, label in entities:
        tx.run("MERGE (e:Entity {name: $name}) "
               "ON CREATE SET e.type = $type",
               name=entity, type=label)
    
    # リレーションの追加
    for subject, relation, object in relations:
        tx.run("MATCH (a:Entity {name: $subject}), (b:Entity {name: $object}) "
               "MERGE (a)-[r:RELATION {type: $relation}]->(b)",
               subject=subject, relation=relation, object=object)

def store_data_in_neo4j(entities, relations):
    with driver.session() as session:
        session.write_transaction(add_entity_and_relation, entities, relations)

# データの格納
store_data_in_neo4j(entities, relations)

driver.close()
```

## 4. 結果の確認とビジュアライゼーション

Neo4j Browserを使用して、格納されたデータを確認します。以下のCypherクエリを実行してください。

```cypher
// すべてのエンティティを表示
MATCH (e:Entity)
RETURN e
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/7928dbe5-55ba-d5f9-474a-49a62c119001.png)


```cypher
// すべてのリレーションを表示
MATCH (a:Entity)-[r:RELATION]->(b:Entity)
RETURN a, r, b
```

```cypher
// エンティティとリレーションの数を確認
MATCH (e:Entity)
WITH count(e) AS entityCount
MATCH ()-[r:RELATION]->()
RETURN entityCount, count(r) AS relationCount
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/19b0febf-d8da-59c4-d871-9a33dab40641.png)
## 5. トラブルシューティング

データが正しく表示されない場合は、以下を確認してください：

1. エンティティが正しく作成されているか
   ```cypher
   MATCH (e:Entity) RETURN e.name, e.type
   ```

2. リレーションの両端のエンティティが存在するか
   ```cypher
   MATCH (a:Entity {name: "山田太郎"}), (b:Entity {name: "人工知能"})
   RETURN a, b
   ```

3. Neo4jのログでエラーメッセージを確認

4. Pythonスクリプト実行時のエラーメッセージを確認

## 6. 発展的トピックと最適化

1. 大規模データ処理のためのバッチ処理実装
2. カスタム関係抽出ルールの追加
3. エンティティ解決（同一エンティティの統合）の実装
4. 時系列データの扱い方
5. 外部知識ベースとの統合

性能向上のためのインデックス作成：

```cypher
CREATE INDEX ON :Entity(name)
```

## まとめ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/2eba81b4-0cae-759f-8da1-06dfc35ab29b.png)

この記事では、テキストからエンティティとリレーションを抽出し、Neo4jグラフデータベースに格納する方法を詳しく解説しました。この技術を応用することで、テキストデータから豊かな知識グラフを構築し、複雑な関係性を視覚化・分析することが可能になります。

実際の応用では、対象ドメインに特化したモデルの訓練やルールの追加、大規模データの効率的な処理など、さらなる最適化が必要になるでしょう。継続的な改善と、使用するテキストデータの特性に応じた調整が重要です。