PHONE APPLI Engineer blog

エンジニアブログ

ナレッジマイニングってなんだ!? Cognitive Search で検索サービスを構築する!

目次

はじめに

こんにちは。 リサーチデベロップメントの後藤です。

先日は、8個の商品がカートに入ったまま、Amazonブラックフライデーが終了しました。 購入に至らなかった商品たち、次のブラックフライデーでお会いしましょう。

さて、今回は調査の一環で Azure の Cognitive Search というサービスを触る機会があったため、こちらの紹介と実際に実装してみた際の所感について執筆していこうと思います!

azure.microsoft.com

Cognitive Search はどんなことができるの?

Azure Cognitive Search とは、Microsoft が提供しているクラウド検索サービスです。

Azure には Cognitive Services という似た名前のサービスがありますが、Cognitive Search とは別物なのでお間違いないように。

(ただ、Cognitive Search 内で、Cognitive Services を使うことができるので、まったく関係性がないわけではないです!)

さて、この Cognitive Search ですが、皆さん、検索サービスと聞いて、どんなことができるのかイメージできますか? (自分は分かりませんでした!)

イメージできなかったかたは、Microsoft Learn で提供されている Cognitive Search のラーニングパスを見てみると、少しだけ正解に近づけるかもしれません。

f:id:Hiromi_Goto:20211209153205p:plain
cognitive search MS learn

Azure Cognitive Search でのナレッジマイニングの実装

ナレッジマイニング、というワードが出てきましたね!!

ナレッジマイニング、皆さんはお聞きになられたことはありますか? (自分は知りませんでした!)

ナレッジマイニングは、データの中に隠されている分析情報にアクセスするプロセスを簡略化するために設計された、新しい AI カテゴリのことです。

ナレッジマイニングについては、こちらのサイトでも説明されていますね!

要約するとナレッジマイニングは、以下の3つのステップで成り立っているようです!

  1. さまざまなソースからコンテンツを取り込む
  2. 情報を抽出したりすることのできる AI 機能によって、コンテンツを強化する
  3. 検索やボットを使用して、強化されたコンテンツを調査する

たどり着くのに時間がかかってしまいましたが...

つまり、Cognitive Search はこのナレッジマイニングができるサービスということですね!

具体的には、さきほどの3つのステップに落とし込むと以下のようなことができるようになるようです!

  1. Cosmos DB や Blob Storage といったさまざまなサービスから、格納されているコンテンツを取り込む
  2. 視覚や音声、言語といった人間の認知 (Cognitive) 機能を有す AI によって、取り込んだコンテンツを強化する
  3. 提供されている API や機能を使用して、強化されたコンテンツを検索する

Cognitive Search に登場する用語たち

前項では、Cognitive Search でできることを掘っていきましたが、次は Cognitive Search に登場する用語について触れ、徐々に実装まで近づいていこうと思います!

Cognitive Search で登場する主要な用語は以下になります。

  • データソース
  • スキルセット
  • インデクサー
  • インデックス

データソース

基データのこと。Azure SQL DatabaseBlob Storageなど、Azure が提供しているサービスをデータソースとして活用した場合、後述するインデクサーを用いたデータのマッピングが行えます。また、これらの Azure サービスを使うことは、Cognitive Search では必須条件ではありません。

インデクサー

インデックスを作成する一連のプロセスを駆動するエンジンのこと。

データソースの値をインデックスにマッピングする工程を担当し、必要に応じて、スキルセットをアタッチできます。

インデックス

JSON形式で構成された、検索クエリの実行先。前項でいうところの、ステップ3の検索対象になります。

スキルセット

インデクサーにアタッチされるさまざまな処理。

インデックスの作成工程で、このスキルセットを組み込むと、ただデータソースからマッピングされたインデックスよりも、強化されたインデックスを作成することが可能になります。

たとえば、画像を解析するスキルを使用した場合、データソースにある画像を検索可能な JSON に変換して、インデックスに格納できるようになります。

ナレッジマイニングを体験する!!

Cognitive Search の概要と用語がわかったところで、最後は、実際に検索サービスを作っていき、ナレッジマイニングを体感していきたいと思います!

Cognitive Search は Azure Portal 上からも作成することができるのですが、今回は JavaScript のSDKを利用して実装をおこなっていきます。

実装の流れ

  1. 基となるデータの用意(今回はダミーデータ)
  2. インデックスの定義と作成
  3. データソースの接続
  4. スキルセットの定義
  5. インデクサーの作成

また、今回使うコードは、冗長ですが分かりやすさのためすべて関数として切り出して記述しています。

基データの用意

まずは、基データを用意しましょう! 今回は、メッセージ送信機能を持つアプリの送信履歴が、 JSON 形式で Blob Storage に格納されている、というケースを想定します。 JSON は以下のような構成で、すでに Azure Blob Storage に格納されているとします。

[
    {
        "id": "1",
        "user": {
            "userId": 1,
            "email": "test@test"
        },
        "message": "吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕えて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。"
    },
    {
        "id": "2",
        "user": {
            "userId": 2,
            "email": "test2@test"
        },
        "message": "ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。その後猫にもだいぶ逢ったがこんな片輪には一度も出会わした事がない。"
    }
]

※ 今回は分かりやすさのため、データの総量は2つとしています。

インデックスの定義と作成

// インデックスの作成
async function createIndex() {
    const { SearchIndexClient, AzureKeyCredential } = require('@azure/search-documents');
    const endpoint = process.env.SEARCH_API_ENDPOINT || "";
    const apiKey = process.env.SEARCH_API_KEY || "";
    const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));

    console.log('Create Index');
    const index = {
        "name": "demo-index",
        "fields": [
            {
                "name": "id",
                "type": "Edm.String",
                "key": true
            },
            {
                "name": "user",
                "type": "Edm.ComplexType",
                "fields": [
                    {
                        "name": "userId",
                        "type": "Edm.Int32"
                    },
                    {
                        "name": "email",
                        "type": "Edm.String",
                        "searchable": true

                    }
                ]
            },
            {
                "name": "message",
                "type": "Edm.String",
                "searchable": true
            },
            {
                "name": "keyPhrase",
                "type": "Collection(Edm.String)"
            }
        ]
    };
    await indexClient.createIndex(index);
    console.log('end')
}

index 定義の部分ですが、注目して欲しいのは、基データの JSON 構成には無かった keyPhrase という項目が追加されている点です。

この keyPhrase には、メッセージから抽出されたキーフレーズが格納されていきます。詳しくは後述するスキルセットインデクサーで説明します。

データソースの接続

次に、Cognitive Search と結びつけるためのデータソースの接続を行います。

今回のデータソースは、Blob Storage ですが、他にもさまざまなサービスに対応しています。

その他のサービスを扱う場合は、type プロパティを変更してください。

// データソースの接続
async function createDataSourceConnection () {
    const { SearchIndexerClient, AzureKeyCredential } = require('@azure/search-documents');
    const endpoint = process.env.SEARCH_API_ENDPOINT || "";
    const apiKey = process.env.SEARCH_API_KEY || "";
    const connectionString = process.env.CONNECTION_STRING || "";
    const indexerClient = new SearchIndexerClient(endpoint, new AzureKeyCredential(apiKey));

    console.log('Create DS Connection');
    const dataSourceConnection = {
        name: 'demo-ds-connection',
        description: "My Data Source 1",
        type: "azureblob",
        container: {
            name: 'json-blob-test'
        },
        connectionString: connectionString
    }
    await indexerClient.createDataSourceConnection(dataSourceConnection);
    console.log('end')
}

スキルセットの定義

次に、スキルセットの定義を行います。 スキルセットは、インデクサーに組み込まれる形で活用されるため、入力(受け取った基 データ)と出力(スキルを使用して変化した結果)の流れを決めていく必要があります。

今回のコードでは、message の値を受け取って、変換の結果を keyPhrase に渡すという流れを定義しています。

//スキルセットの定義
async function createSkillset () {
    const { SearchIndexerClient, AzureKeyCredential } = require('@azure/search-documents');
    const endpoint = process.env.SEARCH_API_ENDPOINT || "";
    const apiKey = process.env.SEARCH_API_KEY || "";
    const indexerClient = new SearchIndexerClient(endpoint, new AzureKeyCredential(apiKey));

    console.log('Create Skillset');
    const skillset = {
        name: "demo-skillset",
        description: "Skillset description",
        skills: [
            {
                odatatype: "#Microsoft.Skills.Text.KeyPhraseExtractionSkill",
                context: "/document",
                defaultLanguageCode: "ja",
                inputs: [
                    {
                        name: "text",
                        source: "/document/message"
                    },
                    {
                        name: "languageCode",
                        source: "/document/language"
                    }
                ],
                outputs: [
                    {
                        name: "keyPhrases",
                        targetName: "keyPhrase"
                    }
                ]
            }
        ]
    }
    await indexerClient.createSkillset(skillset);
    console.log('end')
}

インデクサーの作成

最後に、これまで作成したインデックスやスキルセットを指定したインデクサーを作成します。

async function createIndexer () {
    const { SearchIndexerClient, AzureKeyCredential } = require('@azure/search-documents');
    const endpoint = process.env.SEARCH_API_ENDPOINT || "";
    const apiKey = process.env.SEARCH_API_KEY || "";
    const indexerClient = new SearchIndexerClient(endpoint, new AzureKeyCredential(apiKey));

    // これまで作成した、インデックス、データセット接続、スキルセットの名前を設定
    const indexName = 'demo-index';
    const dataSourceConnectionName ='demo-ds-connection';
    const skillsetName = 'demo-skillset';

    console.log('Create Indexer');
    const indexer = {
        name: 'demo-indexer',
        description: "demo indexer",
        dataSourceName: dataSourceConnectionName,
        targetIndexName: indexName,
        skillsetName: skillsetName,
        parameters: { "configuration": { "parsingMode": "jsonArray" } },
        outputFieldMappings: [
            {
                "sourceFieldName": "/document/keyPhrase",
                "targetFieldName": "keyPhrase"
            }
        ]

    }
    await indexerClient.createIndexer(indexer);
    console.log('end')
}

これで、すべての要素の実装が終わりましたね!

では実際に、Azure Portal 上で作成したインデックスからどのような値を検索できるのか見てみましょう!

作成したインデックスの、「検索エクスプローラー」から、Azure Portal 上で検索を試すことができます。 検索結果は以下になります。

{
    "@odata.context": "",
    "value": [
        {
            "@search.score": 1,
            "id": "1",
            "message": "吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕えて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。",
            "keyPhrase": [
                "",
                "",
                "どこ",
                "",
                "",
                "がつかぬ",
                "",
                "",
                "じめじめ",
                "",
                "している",
                "めて人間",
                "う人",
                "",
                "な種族",
                "えて",
                "て食うという",
                "しかし"
            ],
            "user": {
                "userId": 1,
                "email": "test@test"
            }
        },
        {
            "@search.score": 1,
            "cardId": "2",
            "message": "ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。その後猫にもだいぶ逢ったがこんな片輪には一度も出会わした事がない。",
            "keyPhrase": [
                "ただ",
                "せられて",
                "",
                "持ち",
                "られた時何だかフ",
                "",
                "があっ",
                "ばかり",
                "の上",
                "",
                "を見たのがいわ",
                "る人間というものの見始",
                "ろう",
                "",
                "時妙なものだ",
                "じが今",
                "っている",
                "第一毛をもっ",
                "装飾され",
                "きは",
                "後猫にもだい",
                "がな"
            ],
            "user": {
                "userId": 2,
                "email": "test2@test"
            }
        }
    ]
}

もともと Blob Storage にはなかった、keyPhrase が追加され、検索対象のコンテンツが強化されているのがわかります!

今回のキーフレーズの抽出結果は、あまり正確ではなさそうですが、以前のコンテンツから少し情報が充実しましたね。

キーフレーズの抽出以外にも、エンティティの抽出や画像内のコンテンツ抽出などのスキルも用意されおり、さらに自作したカスタムスキルも組み込めるため、組み合わせによってはさまざまな分析結果が得られそうです。

また、今回は検索の実行を Azure Portal 上から行いましたが、各SDK のメソッドや REST API でもインデックス内にあるコンテンツの検索が可能です。既存のアプリケーションに、今回作成したインデックスを検索する機能を組み込むということも簡単におこなえそうですね!

おわりに

データ総量が少なかったため、今回はあまり各機能の良さを感じられなかったかもしれませんが、これがもし膨大なデータ量だった場合、 Cognitive Search を使った検索機能および、ナレッジマイニングの有用性がより理解できたのではと思います。

Cognitive Search では Free プランも用意されているので、興味がある方は是非ご自身の環境でお試しください!