知識グラフ:AIとデータサイエンスのゲームチェンジャー

知識グラフ:AIとデータサイエンスの革命

導入

知識グラフは、AIとデータサイエンスにおいて、構造化情報を記録し、データの検索、推論、推論を促進するための効果的で多目的な手法として台頭しています。この記事では、構築、表現、クエリ、埋め込み、推論、アラインメント、融合などの最新の知識グラフについて調査します。

また、レコメンデーションエンジンや質問応答システムなど、知識グラフの多くの応用についても議論します。最後に、新たな進歩と研究の機会を開拓するために、このトピックの問題と潜在的な将来の展望について探求します。

知識グラフは、エンティティと特性の間の複雑な関連を表現するための柔軟でスケーラブルなメカニズムを提供することで、情報の組織と利用の方法を革新しました。ここでは、知識グラフの概要、その重要性、およびさまざまな分野での潜在的な利用方法について一般的な導入を行います。

学習目標

  • 知識グラフの概念と目的、情報の構造化表現について理解する。
  • ノード、エッジ、プロパティなど、知識グラフの主要な構成要素について学ぶ。
  • データの抽出と統合技術を含む構築プロセスを探索する。
  • 知識グラフ埋め込みがエンティティと関係を連続ベクトルとして表現する方法を理解する。
  • 既存の知識から新たな洞察を推論するための推論メソッドを探索する。
  • 知識グラフの視覚化による理解向上を探求する。

この記事は、データサイエンスブログマラソンの一環として公開されました。

知識グラフとは何ですか?

知識グラフは、情報抽出操作中に抽出された情報を格納することができます。多くの基本的な知識グラフの実装では、トリプルという概念を利用しています。トリプルは、主語、述語、目的語の3つの要素のコレクションであり、何についての情報を保持できます。

グラフは、ノードとエッジのコレクションです。

これは、設計可能な最小の知識グラフであり、トリプルとも呼ばれます。知識グラフはさまざまな形式やサイズで提供されます。ここでは、ノードAとノードBが個別の要素であることを示すエッジで接続されています。

知識グラフにおけるデータ表現

次のフレーズを例に取り上げます:

ロンドンはイングランドの首都です。ウェストミンスターはロンドンに位置しています。

後で基本的な処理を見ていきますが、最初には次のような2つのトリプルがあります:

(ロンドン、首都、イングランド)、(ウェストミンスター、位置する、ロンドン)

この例では、3つの異なるエンティティ(ロンドン、イングランド、ウェストミンスター)と2つの関係(首都、位置)があります。知識グラフを構築するには、ネットワーク内の2つの関連ノードと関係を持つエンティティと頂点が必要です。その結果得られる構造は次のようになります:知識グラフを手動で作成することはスケーラブルではありません。誰もが数百ページもの文章を読み込んで、すべてのエンティティとそれらの関係を抽出することはありません!

機械は数百、さらには数千の論文を簡単に処理できるため、この作業を人間よりも適しています。ただし、機械は自然言語を理解できないという難しさもあります。この状況では、自然言語処理(NLP)を使用することが重要です。

テキストから知識グラフを作成するためには、コンピュータが自然言語を理解することが重要です。これには、文の分割、依存解析、品詞タグ付け、エンティティ抽出などのNLP手法が使用されます。

依存関係のインポートとデータセットの読み込み

import re
import pandas as pd
import bs4
import requests
import spacy
from spacy import displacy
nlp = spacy.load('en_core_web_sm')

from spacy.matcher import Matcher 
from spacy.tokens import Span 

import networkx as nx

import matplotlib.pyplot as plt
from tqdm import tqdm

pd.set_option('display.max_colwidth', 200)
%matplotlib inline

# Wikipediaの文章をインポートする
candidate_sentences = pd.read_csv("../input/wiki-sentences1/wiki_sentences_v2.csv")
candidate_sentences.shape

candidate_sentences['sentence'].sample(5)

文の分割

テキストの記事や文書を文に分割することは、知識グラフを作成するための最初のステージです。その後、正確に1つの主語と1つの目的語を持つフレーズのみを選択します。

doc = nlp("the drawdown process is governed by astm standard d823")

for tok in doc:
  print(tok.text, "...", tok.dep_)

エンティティの抽出

文の一部である単語を簡単に削除することができます。品詞(POS)タグを使用することで、これを迅速に実現することができます。名詞と固有名詞がエンティティになります。

エンティティが複数の単語にまたがる場合、POSタグだけでは不十分です。文の依存構造木を解析する必要があります。

ノードとその関係は、知識グラフを開発する際に最も重要です。

これらのノードは、Wikipediaのテキストで見つかるエンティティで構成されます。エッジはこれらの要素間の関係を反映します。フレーズ構造からこれらの要素を抽出するために、教師なしアプローチを使用します。

基本的なアイデアは、フレーズを読み、サブジェクトとオブジェクトを見つけることです。ただし、いくつかの欠点があります。例えば、「赤ワイン」はフレーズをまたぐエンティティですが、依存パーサは個々の単語のみをサブジェクトまたはオブジェクトとして識別します。

上記の問題のため、以下のコードを作成し、文からサブジェクトとオブジェクト(エンティティ)を抽出しました。便宜上、コードを多くのセクションに分割しました:

def get_entities(sent):
  ## チャンク 1
  ent1 = ""
  ent2 = ""

  prv_tok_dep = ""    # 文中の前のトークンの依存関係タグ
  prv_tok_text = ""   # 文中の前のトークン

  prefix = ""
  modifier = ""

  #############################################################
  
  for tok in nlp(sent):
    ## チャンク 2
    # トークンが句読点でない場合、次のトークンに進む
    if tok.dep_ != "punct":
      # チェック:トークンが複合語かどうか
      if tok.dep_ == "compound":
        prefix = tok.text
        # 前の単語も「compound」の場合、現在の単語を追加する
        if prv_tok_dep == "compound":
          prefix = prv_tok_text + " "+ tok.text
      
      # チェック:トークンが修飾語かどうか
      if tok.dep_.endswith("mod") == True:
        modifier = tok.text
        # 前の単語も「compound」の場合、現在の単語を追加する
        if prv_tok_dep == "compound":
          modifier = prv_tok_text + " "+ tok.text
      
      ## チャンク 3
      if tok.dep_.find("subj") == True:
        ent1 = modifier +" "+ prefix + " "+ tok.text
        prefix = ""
        modifier = ""
        prv_tok_dep = ""
        prv_tok_text = ""      

      ## チャンク 4
      if tok.dep_.find("obj") == True:
        ent2 = modifier +" "+ prefix +" "+ tok.text
        
      ## チャンク 5  
      # 変数の更新
      prv_tok_dep = tok.dep_
      prv_tok_text = tok.text
  #############################################################

  return [ent1.strip(), ent2.strip()]

チャンク 1

上記のコードブロックでは、いくつかの空の変数が定義されています。前の単語の従属語とその単語自体は、それぞれ変数prv_tok_depとprv_tok_textに保持されます。プレフィックスと修飾語は、主語または目的語に関連するテキストを保持します。

チャンク 2

次に、フレーズ内のすべてのトークンを一つずつ処理します。トークンが句読点であるかどうかをまず確認します。そうであれば、無視して次のトークンに進みます。トークンが複合語の一部である場合(依存関係タグ=「compound」)、それをプレフィックス変数に格納します。

人々は多くの単語を組み合わせて複合語を作り、新しい意味を持つ新しいフレーズを生成します(例:「フットボールスタジアム」や「動物愛好家」など)。

これらのプレフィックスは、文中の各主語や目的語に追加されます。同様の方法は、「素敵なシャツ」、「大きな家」などの形容詞にも使用されます。

チャンク 3

もしサブジェクトがこのトークンである場合、それはent1変数の最初のエンティティとして入力されます。プレフィックス、修飾語、prv_tok_dep、prv_tok_textの変数はすべてリセットされます。

チャンク 4

もしトークンがオブジェクトである場合、それはent2変数の2番目のエンティティとして配置されます。プレフィックス、修飾語、prv_tok_dep、prv_tok_textの変数はすべてリセットされます。

チャンク 5

フレーズの主語と目的語が決まった後、前のトークンとその依存関係タグを更新します。

この関数をテストするためのフレーズを使用しましょう:

get_entities("the film had 200 patents")

うわー、すべて計画通りに進んでいるようです。上記のフレーズでは、「映画」がトピックで、「200の特許」が目的です。

このアプローチを使用して、データ内のすべてのフレーズのエンティティペアリングを抽出できます:

entity_pairs = []

for i in tqdm(candidate_sentences["sentence"]):
  entity_pairs.append(get_entities(i))

リストentity_pairsには、Wikipediaの文章から抽出された主語-目的語のペアが含まれています。いくつかの例を見てみましょう。

entity_pairs[10:20]

ご覧のように、これらのエンティティペアには「私たち」「それ」「彼女」などの代名詞がいくつか存在します。代わりに、固有名詞や名詞が欲しいです。get_entities()のコードを更新して、代名詞を除外することができるかもしれません。

関係の抽出

エンティティの抽出はタスクの半分に過ぎません。ノード(エンティティ)をリンクして知識グラフを形成するためには、エッジが必要です。これらのエッジは、2つのノード間の接続を表します。

私たちの仮説によれば、述語はフレーズ中の主要な動詞です。たとえば、「1929年には60本のハリウッドのミュージカルが公開されました」という文の述語として、「公開されました」という動詞が使用されます。

次の関数は、このような述語を文から抽出することができます。この場合、spaCyのルールベースのマッチングを利用しました:

def get_relation(sent):

  doc = nlp(sent)

  # Matcher class object 
  matcher = Matcher(nlp.vocab)

  # パターンを定義する
  pattern = [{'DEP':'ROOT'}, 
            {'DEP':'prep','OP':"?"},
            {'DEP':'agent','OP':"?"},  
            {'POS':'ADJ','OP':"?"}] 

  matcher.add("matching_1", None, pattern) 

  matches = matcher(doc)
  k = len(matches) - 1

  span = doc[matches[k][1]:matches[k][2]] 

  return(span.text)

この関数のパターンは、フレーズのROOTワードまたは主要な動詞を見つけようとします。ROOTを特定した後、パターンはそれが前置詞(’prep’)またはエージェントワードに続いているかどうかをチェックします。これが当てはまる場合、ROOTワードに追加されます。この関数を実証します:

get_relation("John completed the task")

relations = [get_relation(i) for i in tqdm(candidate_sentences['sentence'])]

抽出した関係または述語の中で最も一般的なものを見てみましょう:

pd.Series(relations).value_counts()[:50]

知識グラフの構築

最後に、取得したエンティティ(主語-目的語のペア)と述語(エンティティ間の関係)を使用して、知識グラフを構築します。エンティティと述語を含むデータフレームを作成しましょう:

# 主語を抽出する
source = [i[0] for i in entity_pairs]

# 目的語を抽出する
target = [i[1] for i in entity_pairs]

kg_df = pd.DataFrame({'source':source, 'target':target, 'edge':relations})

その後、networkxライブラリを使用して、このデータフレームからネットワークを形成します。ノードはエンティティを表し、ノード間のエッジまたは接続はノード間の関係を反映します。

これは有向グラフになります。つまり、リンクされたノードのペアの関係は片方向のみであり、片方のノードからもう一方のノードへのみの関係です。

# データフレームから有向グラフを作成する
G=nx.from_pandas_edgelist(kg_df, "source", "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))

pos = nx.spring_layout(G)
nx.draw(G, with_labels=True, node_color='skyblue', edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

小さな例でネットワークをプロットしましょう:

import networkx as nx
import matplotlib.pyplot as plt

# KnowledgeGraphクラスを作成します
class KnowledgeGraph:
    def __init__(self):
        self.graph = nx.DiGraph()

    def add_entity(self, entity, attributes):
        self.graph.add_node(entity, **attributes)

    def add_relation(self, entity1, relation, entity2):
        self.graph.add_edge(entity1, entity2, label=relation)

    def get_attributes(self, entity):
        return self.graph.nodes[entity]

    def get_related_entities(self, entity, relation):
        related_entities = []
        for _, destination, rel_data in self.graph.out_edges(entity, data=True):
            if rel_data["label"] == relation:
                related_entities.append(destination)
        return related_entities


if __name__ == "__main__":
    # 知識グラフを初期化します
    knowledge_graph = KnowledgeGraph()

    # エンティティと属性を追加します
    knowledge_graph.add_entity("アメリカ合衆国", {"首都": "ワシントンD.C.", "大陸": "北アメリカ"})
    knowledge_graph.add_entity("フランス", {"首都": "パリ", "大陸": "ヨーロッパ"})
    knowledge_graph.add_entity("中国", {"首都": "北京", "大陸": "アジア"})

    # エンティティ間の関係を追加します
    knowledge_graph.add_relation("アメリカ合衆国", "隣国", "カナダ")
    knowledge_graph.add_relation("アメリカ合衆国", "隣国", "メキシコ")
    knowledge_graph.add_relation("フランス", "隣国", "スペイン")
    knowledge_graph.add_relation("フランス", "隣国", "イタリア")
    knowledge_graph.add_relation("中国", "隣国", "インド")
    knowledge_graph.add_relation("中国", "隣国", "ロシア")

    # 属性と関連するエンティティを取得して表示します
    print("フランスの属性:", knowledge_graph.get_attributes("フランス"))
    print("中国の隣国:", knowledge_graph.get_related_entities("中国", "隣国"))

    # 知識グラフを可視化します
    pos = nx.spring_layout(knowledge_graph.graph, seed=42)
    edge_labels = nx.get_edge_attributes(knowledge_graph.graph, "label")

    plt.figure(figsize=(8, 6))
    nx.draw(knowledge_graph.graph, pos, with_labels=True, 
                      node_size=2000, node_color="skyblue", font_size=10)
    nx.draw_networkx_edge_labels(knowledge_graph.graph, pos, 
                                edge_labels=edge_labels, font_size=8)
    plt.title("知識グラフ:国とその首都")
    plt.show()

これはまさに私たちが探していたものではありませんが(それでも非常に見栄えがします!)、すべての関係を持つグラフを生成してしまったことがわかりました。このような多くの関係や述語を持つグラフは、非常に見えにくくなります。

そのため、グラフを可視化するためには、いくつかの主要な関係のみを使用することがベストです。1つの関係ずつ取り組んでみましょう。まずは関係「composed by」から始めましょう:

G=nx.from_pandas_edgelist(kg_df[kg_df['edge']=="composed by"], 
                            "source", "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))
pos = nx.spring_layout(G, k = 0.5) 
nx.draw(G, with_labels=True, node_color='skyblue', 
                                node_size=1500, edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

これはずっと良いグラフです。この場合、矢印は作曲家を指しています。上記のグラフでは、有名な音楽作曲家であるA.R. ラフマンは、「サウンドトラックのスコア」「映画のスコア」「音楽」などと関連付けられています。

他の関連を見てみましょう。次に「written by」の関係のグラフを描画したいと思います:

G=nx.from_pandas_edgelist(kg_df[kg_df['edge']=="written by"], "source", 
                            "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))
pos = nx.spring_layout(G, k = 0.5)
nx.draw(G, with_labels=True, node_color='skyblue', node_size=1500, 
    edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

この知識グラフは、驚くべきデータを提供しています。有名な作詞家には、Javed Akhtar、Krishna Chaitanya、Jaideep Sahniなどが含まれており、このグラフは彼らの関係を見事に描写しています。

さらに重要な述語「released in」に対する知識グラフを見てみましょう:

G = nx.from_pandas_edgelist(kg_df[kg_df['edge']=="リリースされた"],
                           "source", "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))
pos = nx.spring_layout(G, k = 0.5)
nx.draw(G, with_labels=True, node_color='skyblue', node_size=1500,
                       edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

結論

知識グラフは、AIおよびデータサイエンスにおいて、構造化された情報を表現し、効率的なデータの検索、推論、推論を可能にする強力で多目的なツールとして登場しました。この記事を通じて、異なる領域における知識グラフの重要性と影響を強調するためのキーポイントを探求しました。以下に、そのキーポイントを示します:

  • 知識グラフは、ノード、エッジ、プロパティを持つグラフ形式で情報を構造化した表現を提供します。
  • 固定されたスキーマなしで柔軟なデータモデリングを可能にし、異なるソースからのデータ統合を容易にします。
  • 知識グラフの推論により、既存の知識に基づいて新しい事実や洞察を推論することができます。
  • 応用範囲は、自然言語処理、推薦システム、意味検索エンジンなど、さまざまな領域にわたります。
  • 知識グラフの埋め込みは、連続ベクトルでエンティティと関係性を表現し、グラフ上での機械学習を可能にします。

結論として、知識グラフは膨大な相互接続された情報の整理と理解のために不可欠な存在となりました。研究と技術の進展に伴い、知識グラフはAI、データサイエンス、情報検索、意思決定システムなど、さまざまな分野で将来を形作る上で中心的な役割を果たすことでしょう。

よくある質問

この記事で表示されるメディアはAnalytics Vidhyaの所有ではなく、著者の裁量で使用されています。

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more