Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Amazon Bedrock をTeamsとノーコードで連携する

はじめに

10月に入り、やっと秋らしい感じになってきました。
データ分析エンジニアの木介です。
先日、AWS Chatbotの新機能を利用して、BedrockがTeamsやSlackと簡単に連携できるようになったと発表がありました。
今回は、その内容を確認して、BedrockとTeamsを連携する方法について、説明していきます。

aws.amazon.com

概要

Amazon Bedrockとは?

Amazon Bedrock は、AWSが提供するマネージドサービスで、開発者が複数の大規模言語モデル(LLM)を簡単に利用できるプラットフォームです。
Bedrockを使用することで、アプリケーションにAI機能を組み込むために高度なAIやMLの知識を持っていなくても、自然言語処理、テキスト生成、質問応答、翻訳、要約といった高度なAIタスクを実現することができます。

docs.aws.amazon.com

今回のゴール

以下の形でTeamsにAmazon Bedrockで作成したAIエージェントを用いたチャットボットをAWS Chatbotを用いてノーコードで組み込むことが今回の目的になります。

Opening Slide

TeamsへのBedrockを使ったアプリの導入

構築環境

Teamsは、「一般法人向けプラン」と「家庭向けプラン」がありますが、今回は前者の「一般法人向けプラン」の内容を利用します。

www.microsoft.com

以下が利用するAWS サービスです

  • Agents for Amazon Bedrock
    • 生成AIを用いたAIエージェントを作成するために利用
  • AWS Chatbot
    • AWSサービスをTeamsに配信するために利用

また今回はus-east-1でのリージョンで構築を行っていきますが、AWS ChatbotのAmazon Bedrockとの連携はすべてのリージョンでサポートされているとのことです。
料金についてはTeamsにAIエージェントを配信するAWS Chatbotは無料で利用可能ですが、配信されるサービス(今回ではAgents for Amazon Bedrock)には別途費用が必要となります。

aws.amazon.com

aws.amazon.com

1. Agents for Amazon BedrockでAIエージェントを作成する

ではまずAmazon Bedrockを使ってチャットボットで回答をするAIエージェントを作成していきます。
AWSマネジメントコンソールのAmazon Bedrockより「エージェントを作成を選択」します。
以下の画面が出てくるので、任意のエージェント名を入力し作成を選択します。

エージェントの設定については以下の部分のみを修正し、他はデフォルトで設定をしました。

  • モデル:Claude 3 Sonnet
  • ユーザ入力:Enabled
    • 質問に対して情報不足な時に追加情報を求める
  • Memory: Enabled
    • 会話内容を記憶する


上記設定後、保存して終了→準備でテストが可能になります。
作成したAIエージェントのテストが右側のチャット欄から行えます。

作成したAIエージェントで回答が出来ていることが分かります。

2. AWS Chatbotの設定

次にAWS Chatbotでチャットボットの設定を行っていきます。
まずBedrockと連携したいTeamsアカウントからリンクを取得します。
チャネルより右クリックで「チャネルへのリンクを取得」でリンクをコピーします。
AWS Chatbotの設定で必要ですのでメモしておいてください。

AWSマネジメントコンソールのAWS Chatbotの画面よりTeamsを選択して「クライアントを設定」を選択します。

以下の画面で先ほど取得したチャネルへのリンクを入力し、設定を選択。

以下のアクセス許可について承諾が必要となります。

次にチャネルの設定を行っていきます。
以下から「新しいチャネルを設定」を選択します。

Bedrockでのチャネルの設定については以下の形で行っていきます。
画面の「チャネルのURL」項目についてはチャットボットを導入したいTeamsのチャネルのリンクを入力してください。

またアクセス許可のロール設定については「チャネルIAMロール」としました。
これにより、指定したチャネル内のメンバー全員に同じ権限を付与できます。
こちらで作成したロールに後ほどBedrockを使うための権限を付与します。

先ほど作成したロールにBedrockへのアクセス権限を付与していきます。
ロールを選択して、許可を追加から「インラインポリシーを作成」を選択。
以下の形で先ほど作成したエージェントのARNとエイリアスIDを入力します。


{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "AllowInvokeBedrockAgent",
			"Effect": "Allow",
			"Action": "bedrock:InvokeAgent",
			"Resource": [
				"arn:aws:bedrock:us-east-1:111222333444:agent-alias/AGENTARN/ALIASID"
			]
		}
	]
}

以上でAWS Chatbotの設定が完了しました。

3. Teamsの設定

それでは最後にTeams側の設定を行っていきます。
アプリの検索よりAWSを検索し、以下のアプリをチャネルに追加します。
これでTeamsでBedrockを使ったチャットボットが利用できます。


使い方

では早速作成したチャットボットをTeamsで使ってみましょう。
先ほどTeamsに導入したawsアプリでTeams上でAWS CLIの機能が利用可能となります。

docs.aws.amazon.com

まず動作確認として@aws connectorを入力してみましょう。

上記の形で応答があれば成功です。

次に以下のコマンドで利用するエージェントを指定します。
AGENT-IDとALIAS-IDに作成したエージェントのIDを指定します。

@aws connector add ai-chatbot arn:aws:bedrock:us-east-1:111111111111:agent/AGENT-ID ALIAS-ID

以下のレスポンスがあればOKです。

会話については以下のコマンドで行うことができます。

@aws ask ai-chatbot “会話内容”

以下が実際に行った内容です。

手順1で作成したエージェントの設定どおりに、不明点に対して聞き返したり、会話を記憶出来ていることが分かります。

以上でBedrockをTeamsで利用するまでの一通りの流れを紹介しました。
Agents for Amazon Bedrockと連携できるのでKnowledge Basesも扱うことができ、色々なことに応用が出来そうです。

まとめ

今回はノーコードでAmazon BedrockとTeamsを連携する方法を解説しました。
Amazon Bedrock Knowledge Basesを利用することで社内ドキュメントとの連携も可能となり、様々な場面で活用できると感じましたので、皆さんも是非試してみてください。


Acroquest Technologyでは、キャリア採用を行っています。
  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
www.wantedly.com

AWS Lambda(Python)でAmazon Bedrockの出力をレスポンスストリーミング対応してみた

こんにちはイワツカです。
今年の夏は、特に猛暑日が続いていたので、例年にも増して素麺を食べてました。

さて今回は、AWS Lambda(Python)でLambda Web Adapterを用いてレスポンスストリーミングする方法を試してみたので紹介します。

1. 概要

1.1 レスポンスストリーミングとは?

レスポンスストリーミングとは、HTTPリクエストに対してサーバーがレスポンスを一度にまとめて送るのではなく、データを分割して順次クライアントに送信する手法のことです。
AWS Lambdaでは、Lambda関数URLを設定してLambdaからのレスポンスをレスポンスストリーミングにすることができます。
レスポンスストリーミングにすることで、従来に比べてユーザーにレスポンスが届くまでの時間を軽減できるので、Webアプリケーションにおけるユーザー体験を向上させることができます。
用途としては、リアルタイムなチャットのやり取りや、処理の進行状況などを表示するのに便利です。

ただ、現在レスポンスストリーミングに直接対応しているのは、ランタイムがNode.jsの場合のみです。 docs.aws.amazon.com

Lambdaで使用するランタイムがPythonの場合は、Lambda Web Adapterを使用することでレスポンスストリーミングを実現可能です。

1.2 Lambda Web Adapterとは?

Lambda Web Adapterとは、ウェブアプリケーションAWS Lambda上で実行するためのツールです。
Lambda Web Adapterを使用するとFastAPIなどのフレームワークを利用できるようになります。
github.com

そしてFastAPIには、StreamingResponseという、レスポンスストリーミングを実現するための機能があります。
fastapi.tiangolo.com

そのため、Lambda Web AdapterとFastAPIを組み合わせることによって、Pythonで実装をするLambdaでも、レスポンスストリーミングを実現できるようになります。

2. アプリ作成

この記事では、Lambda Web Adapterを使ってFastAPIをLambda上で動かし、Amazon Bedrockを使ったLLM(大規模言語モデル)とのチャットアプリをレスポンスストリーミングで作成します。
構成や実装はAWSの公式の例を参考に実施しました。 github.com

全体像はこちらです。StreamlitというPythonフレームワークで画面を作成し、LLMとのチャットをできるようにしています。

作成するアプリの全体像

2.1 実行環境

本アプリを動かすには、FastAPIを動かすためのコンテナをビルドするためのDocker環境とStreamlitを動かすためのPython環境が必要です。
Python環境は、Python 3.12.4、Streamlit 1.38.0を使用しました。

2.2 ディレクトリ構成

ディレクトリ構成は、AWS公式の例に載っているファイルに加えて、Streamlitのスクリプトのみを追加しています。
Dockerfile、requirements.txt、template.yamlは本アプリでも同じ内容でそのまま利用しました。

./
  |--app/
  |  |--Dockerfile
  |  |--main.py
  |  |--requirements.txt
  |
  |--streamlit_app.py
  |--template.yaml

2.3 FastAPIの実装

FastAPIの実装(main.py)では、APIのレスポンスをStreamingResponseにすることがポイントです。
また、StreamingResponseに渡すbedrock_chat_stream()の処理では、画面から受け取ったユーザーのメッセージに対して、Bedrockからの応答をreturnでなくyieldで都度、返しているところも重要です。
基本的な実装は公式の例に載っているので、比較的簡単に実装することができました。

import boto3
import json
import os
import uvicorn
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from typing import Optional


# FastAPIのインスタンスを作成
app = FastAPI()


# チャットメッセージのモデル
class ChatMessage(BaseModel):
    message: Optional[str] = None


# APIエンドポイントを定義
@app.post("/api/chat")
def api_chat(chat_message: ChatMessage):
    if chat_message.message is None or chat_message.message.strip() == "":
        return {"error": "Message cannot be empty"}

    return StreamingResponse(bedrock_chat_stream(chat_message.message), media_type="text/event-stream")


# Bedrockクライアントを初期化
bedrock = boto3.client('bedrock-runtime')


async def bedrock_chat_stream(user_message: str):
    """ユーザーのメッセージを受け取り、LLMからの返答をストリーミング形式で返す"""
    instruction = f"ユーザーメッセージに対する会話を続けてください: {user_message}"

    body = json.dumps({
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": 1024,
        "messages": [
            {
                "role": "user",
                "content": instruction,
            }
        ],
    })

    # Bedrockからレスポンスストリーミングを取得
    response = bedrock.invoke_model_with_response_stream(
        modelId='anthropic.claude-3-haiku-20240307-v1:0',
        body=body
    )

    # ストリームが存在する場合、逐次データを処理
    stream = response.get('body')
    if stream:
        for event in stream:
            chunk = event.get('chunk')
            if chunk:
                message = json.loads(chunk.get("bytes").decode())
                if message['type'] == "content_block_delta":
                    yield message['delta']['text'] or ""
                elif message['type'] == "message_stop":
                    yield "\n"


if __name__ == "__main__":
    # Uvicornを使用してサーバーを起動
    uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))

2.4 Streamlitの実装

Streamlitの公式ページに載っているLLMとのチャット画面を参考に実装しました。 docs.streamlit.io

Streamlitでストリーミング出力させる場合はst.write_stream()を利用します。
本アプリでは、LLMからの応答をrequestライブラリを通して取得していて、st.write_stream()が対応していない型になっているため、対応させるためにラッパー関数(stream_wrapper())を追加しています。

import requests
import streamlit as st


# FastAPIのエンドポイントURL
API_URL = "<Lambda関数URL>"

# 画面にタイトルを追加
st.title("AI Chat with Bedrock")

# チャット履歴のsession_stateを初期化
if "messages" not in st.session_state:
    st.session_state.messages = []

# チャット履歴を表示
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])


def stream_wrapper(response):
    """レスポンスをStreamlitで互換性のある形式に変換する"""
    for chunk in response.iter_content(chunk_size=128):
        if chunk:
            yield chunk.decode('utf-8')


# チャット入力
if prompt := st.chat_input("What would you like to discuss?"):
    with st.chat_message("user"):
        st.markdown(prompt)
    # チャット履歴にユーザー入力を追加
    st.session_state.messages.append({"role": "user", "content": prompt})

    # APIにリクエストを送信し、レスポンスストリーミングを受け取る
    with st.chat_message("assistant"):
        response = requests.post(API_URL, json={"message": prompt}, stream=True)
        response_text = st.write_stream(stream_wrapper(response))

    # チャット履歴にAPIのレスポンスを追加
    st.session_state.messages.append({"role": "assistant", "content": response_text})

3. アプリを動かして見る

本アプリは以下の2ステップで動かせます。
1. チャットアプリをデプロイ
2. Streamlitを起動する

3.1 チャットアプリをデプロイ

AWS公式の例に載っている手順でFastAPIを動かすLambdaのビルドとデプロイを行います。
そして、デプロイ後にLambda関数URLが分かります。

sam build --use-container
sam deploy --guided

以下は利用したtemplate.yamlです。AWS公式の例に載っているtemplate.yamlと同じ内容です。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  Streaming Chat with FastAPI on AWS Lambda

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 300

Resources:
  FastAPIFunction:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      MemorySize: 512
      Environment:
        Variables:
          AWS_LWA_INVOKE_MODE: RESPONSE_STREAM
      FunctionUrlConfig:
        AuthType: NONE
        InvokeMode: RESPONSE_STREAM
      Policies:
      - Statement:
        - Sid: BedrockInvokePolicy
          Effect: Allow
          Action:
          - bedrock:InvokeModelWithResponseStream
          Resource: '*'
      Tracing: Active
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./app
      DockerTag: v1

Outputs:
  FastAPIFunctionUrl:
    Description: "Function URL for FastAPI function"
    Value: !GetAtt FastAPIFunctionUrl.FunctionUrl
  FastAPIFunction:
    Description: "FastAPI Lambda Function ARN"
    Value: !GetAtt FastAPIFunction.Arn

3.2 Streamlitを起動する

上記のデプロイで得られたLambda関数URLをstreamlit_app.pyのAPI_URLに代入します。
その後、streamlit_app.pyがあるディレクトリで以下のコマンドを実行します。
streamlit run streamlit_app.py

3.3 チャットを試してみる

それでは実際にチャットを試してみます。
LLMからの応答が逐次表示されているのが分かります。
※なお、本アプリはレスポンスストリーミングを試すために作成したので、会話履歴はLLMに入力しておらず単発の会話のみ可能です。

レスポンスストリーミングのチャット画面

4. まとめ

今回はLambda Web Adapterを使ってLambda関数URLの出力をレスポンスストリーミングにする方法を試してみました。
レスポンスストリーミングがAWS Lambdaでも可能になるので、チャットアプリの実現等、Webアプリケーションの幅が広がりますね。
AWSの公式のサンプルやドキュメントが充実していて、比較的簡単に実装することができるので、ぜひ試してみてください。

Acroquest Technologyでは、キャリア採用を行っています。
  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
  少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com

データマイニングの国際学会KDD2024@スペインでは、やはりLLM関連の話題が豊富

皆さんこんにちは
機械学習チームYAMALEX@tereka114です。
YAMALEXは Acroquest 社内で発足した、会社の未来の技術を創る、機械学習がメインテーマのデータサイエンスチームです。
(詳細はリンク先をご覧ください。)

KDD主催のコンペティションKDDCupで入賞したことより、昨年に引き続き、KDDに参加しましたのでそのレポートを記載させていただきます。
今年はスペイン・バルセロナでの開催だったため、ヨーロッパへ初渡航でした。楽しかったです!

会場写真

※昨年のレポートはこちら
acro-engineer.hatenablog.com

KDD2024

KDDは正式名称、30th ACM SIGKDD CONFERENCE ON KNOWLEDGE DISCOVERY AND DATA MININGです。
8月25日(日)〜29日(木)主なテーマとしてデータマイニングを扱うカンファレンスです。

今回の会議としてはKeyNote3本、各セッションへの参加、Workshop/Tutorialへの参加など盛りだくさんでした。
昨年と変わらず、リモートでの参加はできず、基本現地への参加になるものでした。

KDDには主に、SIGKDD Conference(本会議)、KeyNote、Workshop/Tutorialに加え、各論文を説明するセッションやポスター発表から構成されています。
今年のセッション部は10ほどのテーマごとに常時開催されていて、合計で約180のセッションが開かれ、非常に盛り上がりました。

また、KDDで開催されるコンペティションのKDDCupはテーマを変えて毎年開催されており、結果はKDDCupのWorkshopで発表されます。
今年はアカデミックな論文の関係性を示すグラフの問題をLLMで解く、RAGを利用したもの、オンラインショッピングのAnswerを回答するネタがあり、私達はオンラインショッピングに参加していました。

kdd2024.kdd.org

Opening

今年のKDDのOpening Sessionです。
参加者は昨年と同じ程度の2300人の参加でした。
同じタイミングでBest Paperをはじめとした賞の紹介もありました。

Opening Slide

Best Paperは

Tutorial/Workshop

LLMに関連する研究が非常に早いスピードで進化していますので、そのキャッチアップのために参加しました。
LLMで最も有名な精度向上方法であるRAGを利用したOverview、また、LLMを利用した時系列や空間/時間に関する基盤モデル構築の話もあり、LLMで実施されていることが他の分野にも進出している様がありました。

RAG Meets LLMs: Towards RetrievalAugmented Large Language Models

LLM関係で最も有名な技術としてRAG(Retrieval Augmented Generation)があります。
RAGに関係する論文も多くこの1年で発表されたので、RAGに関する技術内容を体系立てて、一般的な課題や関連手法に関する説明がありました。
私自身、1年間RAGに関連する技術が高速に発展したと感じていますので、このワークショップでその技術の整理ができました。
また、LLMを利用した具体的なアプリケーションにおける用途も説明されているため、どのようなものに利用できるかアイデアも膨らませられ、面白かったです。

※ワークショップページはこちら、ページにはスライドもあるのでおすすめです。
advanced-recommender-systems.github.io

The 10th Mining and Learning from Time Series Workshop: From Classical Method to LLMs

このワークショップでは、古典的な手法からLLMでの時系列モデリング話まで含まれていました。
LLMといえば、チャットサービスかRAGの分野を一般的に頭に思い浮かべますが、時系列予測にLLMを応用する論文が紹介されていました。
例えば、次の論文にあるLLMの一つであるT5と人工データを利用して時系列データを事前学習させるとZeroshotで新しいデータに対しても時系列推論ができるのは面白かったです。
更に、このモデルを使ってより特定のデータをFinetuneして精度を向上させることも可能です。
時系列に対してトークンを生成すると、そもそも通常の学習では学べるはずの数値の上下関係(例:0.2 > 0.1)が学習できないので若干の違和感を感じますが、データの量と事前学習がここまでの効果を発揮するのは驚きです。

また、こちらの論文のモデルは公開されているため、すぐに利用できるので試す機会があれば、利用しようと思っています。

arxiv.org

huggingface.co

※ワークショップページはこちら
kdd-milets.github.io

KDDCup2024

私達がKDDCup2024で参加した今年の問題はAmazonのオンラインショッピングに関する問題を解くスコアを競うものでした。
それぞれのテーマで分割されているTrack1〜Track4の独立した問題セットとすべてを一つのモデルで解くTrack5がありました。
詳しくは次の公式サイトをご確認ください。

www.aicrowd.com

また、本コンペティションには当社のチームも出場し、入賞しました。

KDDCupの順位表

本KDDCupのWorkshopでは上位チームの解法が話されていました。
個人的な学びポイントは次のとおりです。

1. 基盤モデルの選定が最も重要、案の定パラメータが大きなモデルが勝ちやすい。
2. 推論環境がT4x4の場合、AWQによりパラメータを削減することで72Bでも高速な推論(推論時間)が可能である。
3. vllmによる高速推論、huggingfaceよりも遥かに高速で便利である。

KDD Annual Celebration

毎年、開催されているKDDのパーティーに参加しました。
今年のカンファレンスの総合情報、スポンサーによるプレゼン、来年のカンファレンスの会場(カナダ・トロント)の紹介がありました。

来年の会場アナウンス

その後は立食パーティーになりますので、歩きながら他の参加者とも話していました。

立食パーティーのご飯

その他

もう少しで建設が完了するサグラダ・ファミリアカタルーニャ歴史博物館にも行きました。
特にサグラダ・ファミリアに入るには予約が必要です。なかなか取りづらいので、サグラダ・ファミリア公式アプリによる事前予約がオススメです。

サグラダ・ファミリア

総括

LLM2年目ともあり、LLMの研究も少し方向性が変わってきました。

1. 昨年はLLMとは?といった基本的なものでしたが、RAGや既存モデルを利用した応用が増えた。
2. LLMのトラックが新設されて独立しており、関連する多くの論文発表があった。
3. LLMを応用した他分野への基盤モデルの作成

最後に

KDD2024はご飯、観光先の多いスペインともあり、立地も含め、総合的に非常に満足度の高い学会でした。
学会では、特にLLMが多く変わらず注目度が高いと感じました。
来年もKDDCupで勝って参加したいと思います。


Acroquest Technologyでは、キャリア採用を行っています。
  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
www.wantedly.com

RAGの処理で、リランクとベクトル検索でできることの違いを検証/解説してみる

こんにちは。テニスしすぎて日焼けがすごいSsk1029Takashiです。

私は普段、生成AIを活用したRAGソリューションの開発をしているのですが、RAGでは特に検索部分の調整が重要になります。
今回はその検索の中で出てくるリランクに焦点を当てて、ベクトル検索と比較してどのような特徴があるのかというところを、検証を交えて解説していきます。

概要

RAGの検索部分では、よくベクトル検索が利用されます。
理由としては、入力が基本的に質問形式になりキーワードで入力されることが多い通常の検索よりも適している、などいくつか考えられます。

ただし、実際にRAGを試してみるとわかりますが、RAGシステムではベクトル検索だけでは検索精度の面で苦労することも多いです。
そこで解決方法の一つとして考えられているのが、ベクトル検索とリランクとの併用になります。
今回は、なぜRAGはベクトル検索だけだと苦労が多いのか、その苦労をリランクが解決できるのかを、実装・検証を交えてみていきます。

リランクとは?

まず、検索におけるリランクについて簡単に説明しておきます。
リランクとは一度検索した結果を再度順位付けすることを指します。

どのような効果があるかというと、より詳細にほしい情報を結果の上位に持ってくることが可能になります。
手法自体は様々なものがありますが、ここではリランカ―モデルを使用したものを例にします。
リランクの特徴として、より詳細に入力クエリの意図に近いものを上位に持ってくることができるというものがあります。

ただし、リランクは質問とドキュメントごとにスコアを計算する都合上、処理速度が遅く検索対象全体に対して実行することは推奨されていません。
反面、ベクトル検索は精度に関してはリランクより劣りますが、処理速度が速いため、検索対象全体に対しても問題なく検索できます。

そのため、今現在の主流は以下のように組み合わせて利用する手法になります。
①ベクトル検索でおおまかに質問に近いトピックの文章を広く検索して上位100件など多めに結果を取得する
②ベクトル検索の取得結果を、リランクを使って並べ替える。

ここで疑問になるのは、リランクがベクトル検索よりも精度が良いというのは、具体的にどのような違いがあるかということです。
なので、次の章ではリランクはベクトル検索と比較してどのように精度が良いのかというところを検証してみましょう。

実験

環境・使用するモデル

今回の実験はcollaboratory上で実施します。
使用するモデルはベクトルにはmultilingual-e5-baseを使用します。
日本語に対応しており、精度も日本語では他モデルに対して比較的良いとされています。
huggingface.co
リランカーモデルではjapanese-reranker-cross-encoder-large-v1を使用します。
huggingface.co
このモデルは日本語に特化した数少ないリランク専用のモデルになります。

実験内容

今回は宿のレビュー検索を想定した簡単な実験を行います。
まず、以下のレビュー文章を用意します。

No 文章
1 この宿の温泉は、まさに極上の癒しでした。源泉かけ流しの湯はとても柔らかく、肌がすべすべになる感じがしました。露天風呂からは美しい山々の景色が広がり、夜には満天の星空を眺めながらゆっくりと浸かることができました。何度でも訪れたくなる温泉です。
2 宿の温泉はとても気持ち良く、リラックスできました。内湯と露天風呂があり、それぞれ異なる趣があります。特に露天風呂から見える庭園の景色が素晴らしく、四季折々の美しさを楽しむことができます。お湯も適温で、長時間浸かっていても疲れませんでした。
3 温泉が自慢の宿とのことで期待していましたが、期待以上でした。天然温泉の源泉をそのまま使用しているので、お湯の質がとても良く、温まった後もポカポカが続きました。貸切風呂も予約して利用しましたが、プライベートな空間で贅沢なひとときを過ごすことができました。
4 温泉は広々としていて、開放感が抜群でした。露天風呂からは海が一望でき、波の音を聞きながらゆっくりと過ごすことができました。お湯も熱すぎず、じっくりと温まることができてとても満足です。また、日帰り利用も可能なので、気軽に訪れることができるのも嬉しいポイントです。
5 温泉は硫黄の香りが心地よく、いかにも温泉に来たという実感がありました。お湯の効能も高く、肌がツルツルになるのを感じました。複数の湯船があり、時間帯によっては貸切状態になることもあり、贅沢な気分を味わえました。また、湯上がりに提供される冷たいドリンクも嬉しいサービスでした。
6 宿の食事はまさに芸術品のようでした。地元の新鮮な食材をふんだんに使った会席料理は、見た目も美しく、一品一品が丁寧に作られているのが伝わってきました。特に旬の魚介を使った刺身は絶品で、これだけでもまた訪れたいと思いました。
7 夕食は地元の名物料理が盛りだくさんで、大満足でした。特に炭火焼きでいただいた和牛ステーキは口の中でとろける美味しさで、何度もおかわりしたくなるほどでした。朝食も種類が豊富で、地元の野菜を使ったサラダや、手作りの豆腐が美味しかったです。
8 夕食はコース料理で、どれも美味しかったのですが、特に印象に残ったのは地元で採れた野菜を使った前菜と、自家製のデザートです。食材の味を活かしたシンプルな調理法で、素材の良さが際立っていました。朝食もバランスが良く、特に焼きたてのパンが絶品でした。
9 宿の食事は期待以上でした。海の近くということもあり、新鮮な魚介類が豊富に使われていて、お刺身や煮魚がとても美味しかったです。夕食は量もたっぷりで、どの料理も心のこもった味付けでした。朝食の和食もとても美味しく、特に温泉卵が絶品でした。
10 夕食は地元の食材をふんだんに使った創作料理で、どの料理も工夫が感じられました。特に地元産の野菜とお肉を使ったグリル料理が絶品で、素材の味がしっかりと引き立っていました。朝食も手作りのジャムや焼き立てのパンなど、こだわりが感じられる内容で、大変満足しました。

上記のレビュー文章は1~5は温泉について言及していて、6~10は食事について言及しているレビューです。
このレビュー記事から「お肉の料理がおいしい宿」というクエリが入力される場合を考えます。
想定としては上記の中だとNo. 7とNo. 10の記事が取得できることを期待したいですが、ベクトル検索とリランクの結果がそれぞれどうなるのかを比較してみます。

ベクトル検索

ベクトル検索ではe5-baseを使用して変換したベクトルを、faissを使ってベクトル検索します。
faissで検索する部分はIndexFlatL2を使用して検索するだけなので、コードは省略します。
IndexFlatL2はL2ノルムを利用した逐次検索のため、シンプルに全件に対してL2距離を算出します。

import torch.nn.functional as F

from torch import Tensor
from transformers import AutoTokenizer, AutoModel

def average_pool(last_hidden_states: Tensor,
                 attention_mask: Tensor) -> Tensor:
    last_hidden = last_hidden_states.masked_fill(~attention_mask[..., None].bool(), 0.0)
    return last_hidden.sum(dim=1) / attention_mask.sum(dim=1)[..., None]

input_texts = [
    <各文章のリスト>
]

# モデルを定義
tokenizer = AutoTokenizer.from_pretrained('intfloat/multilingual-e5-base')
model = AutoModel.from_pretrained('intfloat/multilingual-e5-base')
# ベクトルに変換した結果を取得 
batch_dict = tokenizer(input_texts, max_length=512, padding=True, truncation=True, return_tensors='pt')
outputs = model(**batch_dict)
embeddings = average_pool(outputs.last_hidden_state, batch_dict['attention_mask'])
embeddings = F.normalize(embeddings, p=2, dim=1)

検索の結果としてトップ5件を並べると以下のようになりました。
この場合L2距離がスコアになるため、低いものほど近しいと判定されたドキュメントになります。

レビューNo. スコア
6 0.26516113
9 0.28850213
7 0.28850213
10 0.28850213
2 0.28850213

おおむね食事について言及しているレビューを取得できていますが、本来はトップに来てほしい肉料理について言及しているNo.7は3番目の表示順になっています。
ベクトル検索ではこのようにクエリの意図からは異なる文章がトップになることがよくあります。

リランクの場合

リランクの場合を見てみましょう。
リランクはクエリ+レビュー文章を入力にして、10件分それぞれのスコアを算出し、スコアが高いものほど正しいと判断されたデータになります。

from sentence_transformers import CrossEncoder
import torch

# モデルを定義する
MODEL_NAME = "hotchpotch/japanese-reranker-cross-encoder-large-v1"
device = "cuda" if torch.cuda.is_available() else "cpu"
model = CrossEncoder(MODEL_NAME, max_length=512, device=device)

if device == "cuda":
    model.model.half()

query = "お肉の料理がおいしい宿"
# 各レビュー文章に対してスコアを算出する
scores = model.predict([(query, passage) for passage in passages])

そうすると上位5件の結果は以下のようになります。
リランクの場合はベクトル検索とは異なり、スコアが高いものが上位のドキュメントを表します。

レビューNo. スコア
7 0.5723
10 0.5264
6 0.2274
9 0.08154
8 0.01009

肉料理について言及しているNo. 7とNo. 10が上位に来ており、さらにスコアでもかなり他と差がついていることがわかります。

結果

結果としてはベクトル検索を使用した場合はざっくり同じトピックの文章は上位に持って来れていますが、ほしい文章ではないものがトップに来ていたのに対して、リランクではほしい文章をトップに持ってくることができました。
ただし、リランクは前述した通り、対象のドキュメントごとにモデルを使ってスコアを計算するため、メモリなどのリソースが必要でなおかつ時間がかかります。
そのため、大量のドキュメントからおおむね同じトピックについて言及しているドキュメントをベクトル検索で広く取って、取得した結果をリランクするという手法がRAGにおける検索の主流になっています。

まとめ

この記事では、リランクに焦点を当ててベクトル検索とどのように異なるのかを検証してみました。
ベクトル検索とリランクは明確にできることが異なるため、うまく組み合わせて使うことが必要になります。
RAGなどが最近よく使われるようになった影響でさらに研究が進んでいる技術なので今後にも注目ですね。

それではまた。

Acroquest Technologyでは、キャリア採用を行っています。

  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com

Difyで作成した生成AIチャットをWebアプリに組み込む

はじめに

9月に入って、少し涼しくなったと思ったら、また、35℃などになる暑い日が続きますね。
データ分析エンジニアとして毎日充実した時間を過ごしている木介です。
今回はGUIで簡単に生成AIアプリを作成できるDifyで作成したAIアプリを簡単にWebアプリに組込む方法について紹介します

docs.dify.ai

Difyとは?

概要

Difyは、生成AIを活用したアプリケーションをGUIで簡単に開発することができるアプリです。

以下のような形で簡単に生成AIを使ったワークフローを作成できます。

詳細な解説については以下の記事を是非参考にして頂ければと思います。

acro-engineer.hatenablog.com

今回作成するアプリ

以下の画像のような形で、Difyで作成したアプリをチャットボットとしてWebアプリに組み込んだものを作成します。

埋め込む方法については以下の三種類が存在します。

  1. iframeで埋め込む
    • 任意の場所に埋め込むことができる
  2. htmlで埋め込む
    • 右下にチャットボットの形で埋め込まれる
  3. Chrome拡張機能で埋め込む

それぞれに特徴があるので用途に合わせて使い分ける形になると思います。
今回は1,2のiframeとhtmlで埋め込む方法についてそれぞれ紹介します。

Difyを埋め込んだWebアプリの構築方法

利用するツール

今回は以下のツールを使って、Webアプリを作成していきます。
DifyはDockerで構築したものを利用していきます。
Difyで作成した生成AIアプリを埋め込むWebアプリについてはStreamlitを使って作成していきます。

  • Dify: 生成AIアプリを作成する
    • Docker版
    • バージョン: 0.8.2
  • Streamlit: Pythonを使って簡単にWebアプリを作成できる

streamlit.io

構築方法

1. DifyによるAIアプリの構築

では早速Difyを使ってAIアプリを作成していきましょう。
Difyの構築方法については以下の記事で紹介していますので割愛させていただきます。

acro-engineer.hatenablog.com

今回のAIアプリはチャットボットとして、以下の形でシンプルに作成しました。
生成AIについては、Amazon BedrockからClaude 3 Sonnetを用いています。


2. StreamlitによるDifyを埋め込んだWebアプリの作成

ではまず、WebアプリにAIアプリを埋め込むためのiframeとhtmlを取得します。
取得方法は簡単で以下の「公開する」タブを押します。

そして「公開する」を選択したあと、「サイトに埋め込む」を押します。

以下の画面が出てきますので、左側を選択することで、iframeを取得できます。

真ん中を選択することで以下のようにhtmlを取得することができます。

上記で取得したコードを利用することで、WebアプリにDifyのアプリを組み込んでいきます。

ではStreamlitで、Webアプリの作成を行っていきます。
まず以下のコマンドでStreamlitをインストールしてください。

pip install streamlit

次にapp.pyファイルを作成して、以下のコードを実装します。
動作の実験用に、iframe版とHTML版の両方のコードを1つの画面に表示するようにしています。実際の利用時はどちらかだけでOKです。

import streamlit as st
import streamlit.components.v1 as stc

def embedding_dify_iframe():
    st.write("## iframeで埋め込む")
    # iframeのHTMLコード
    html_code = """
    Difyから取得したiframeコードを貼り付けてください
    """

    # HTMLをStreamlitアプリに埋め込む
    stc.html(html_code, height=700)

def embedding_dify_html():
    st.write("## HTMLとJavaScriptを埋め込む")
    # HTMLとJavaScriptを埋め込む
    html_code = """
    Difyから取得したHTMLコードを貼り付けてください
    """

    stc.html(html_code, height=500)

if __name__ == "__main__":
    st.title("Embedding Dify")
    
    # iframeで表示
    embedding_dify_iframe()
    
    # HTMLで表示
    embedding_dify_html()

以上でDifyで作成したAIアプリを埋め込んだWebアプリの作成が完了しました。
Streamlitのおかげで非常に簡単にアプリの作成まで出来てしまいました。

作成したアプリの実行

では作成したアプリを早速実行していきます。
以下のコマンドでWebアプリが立ち上がります。

streamlit run app.py 

http://localhost:8501にアクセスするとアプリの画面が表示されるはずです。

以下の画面がiframeで埋め込んだAIアプリです。

以下のような形でチャットボットが機能していることが確認できます。

ページ下部に以下のようにHTMLを使って埋め込んだAIアプリがあります。

以下のような形で同様にチャットボットが機能していることが確認できました。

以上がDifyを使って作成したAIアプリを簡単にWebアプリに組み込む方法の紹介になります。
非常に簡単にでき、iframeとHTMLを使ったもので使い分けができるので、様々な場面に利用出来そうです。

まとめ

今回はDifyを使って作成したAIアプリを簡単にWebアプリに組み込む方法について紹介しました。
Difyで作成したアプリを簡単に自身のWebアプリに組み込むことができるので、是非皆さんも触っていただければと思います。


Acroquest Technologyでは、キャリア採用を行っています。
  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
www.wantedly.com

Dify v0.8.0でパラレル処理(並列処理)を活用したワークフローを作成する

こんにちは、バックエンドエンジニアの前田です。
先日、以前自分が書いたコードのコメントが何を伝えたいのかがわからず、いかにわかりやすいコメントを書くことが大事かを身に染みて痛感しました。

さて、今回はDifyのアップデートで新規追加された機能「パラレル処理(並列処理)」を触っていきます。

1. 概要

1.1. Difyとは

Difyは、大規模言語モデル(LLM)を用いたオープンソースアプリ開発プラットフォームです。
詳しくは、以下の記事で紹介されているので、参考にしてみてください。
acro-engineer.hatenablog.com

1.2. Dify v0.8.0の新機能について

Difyは9/10にアップデートがあり、v0.8.0がリリースされました。
v0.8.0で新規追加された機能は「パラレル処理(並列処理)」。
今までも、分岐や順次実行はできていましたが、今回のアップデートにより、同時に処理を走らせることができるようになり、より効率的なフローを組むことができるようになりました。
詳しくは以下の公式ページに4つの並列処理の例が載っています。
docs.dify.ai

2. アプリ作成

2.1. この記事で実現すること

この記事では、並列処理を活用して、複数LLMの回答を比較し、差分をとるアプリを作っていきます。

比較するLLMは以下の3つです。

  • GPT4o
  • GPT4o-mini
  • Claude3.5-sonnet
2.2. パラレル処理(並列処理)を用いてLLMを呼び出す

まずは、ユーザーからの質問に対して複数のLLMが並列に回答を生成する処理を作っていきます。
作り方はとても簡単で、「開始」ブロックの「+」ボタンをクリックして、「LLM」ブロックを追加するだけ。


モデルを選択し、プロンプトには、ユーザーからの質問をそのまま記載しておきます。

同じようにGPT4o-miniとClaude3.5-sonnetも追加して、完了です。

2.3. 各LLMからの回答を比較する

次に、各LLMから得られた回答を一つにまとめて、回答を比較する処理を作っていきます。
こちらの作り方も簡単で、「LLM」ブロックをもう一つ追加して、各LLMから線をつなぐだけ。

あとは「回答」ブロックと繋いでアプリ完成です。
総作成時間は5分ほどで、簡単に並列処理を実装することができました。

3. 実際にアプリを動かしてみる

では、実際に動かしてみます。
明日の晩御飯でも考えてもらいましょう。

Q:明日の晩御飯を考えて

A:※各LLMの回答は長いので割愛
差分比較
比較結果
「詳細なレシピ vs 簡易的なメニュー提案」
差分:


GPT4oは詳細なレシピを提供しています。
メインディッシュ: 照り焼きチキン
材料リストと具体的な作り方を記載。
サイドディッシュ1: ほうれん草のお浸し
材料リストと具体的な作り方を記載。
サイドディッシュ2: 味噌汁
材料リストと具体的な作り方を記載。
デザート: フルーツサラダ
材料リストと具体的な作り方を記載。


GPT4o-miniは簡易的なメニュー提案を行っています。
メインディッシュ:鶏肉の照り焼き
鶏もも肉を照り焼きソースで焼くと簡単に説明。
サイドディッシュ:ほうれん草のおひたし
ほうれん草を軽く茹でて、だし醤油やごまをかけると簡単に説明。
スープ:味噌汁
豆腐とわかめ、ネギを入れた味噌汁を簡単に説明。
ご飯:白ご飯
ふっくら炊きたての白ご飯を添えると簡単に説明。
デザート:季節のフルーツ
季節のフルーツを提案。


Claude3.5-sonnetは複数のメニューアイデアを提案しています。
和食
鮭の塩焼き、ほうれん草のお浸し、味噌汁、白米。
洋食
チキンソテー with レモンバターソース、ローストベジタブル、サラダ、パン or ライス。
中華
麻婆豆腐、青梗菜の炒めもの、中華スープ、白米。
イタリアン
パスタ カルボナーラルッコラとトマトのサラダ、ガーリックブレッド。
ヘルシーオプション
グリルチキン、キヌアサラダ、蒸し野菜、ヨーグルトディップ。

全てのLLMが並列で動いていることも確認できました。

従来のフローと比べてどれくらい効率的になったのかも気になるところ。
そこで、以下のような直列処理(従来のフロー)を作って、実行時間を比較してみました。

結果は以下のようになりました。

直列処理 並列処理
実行時間(s) 33.4 9.7

▼直列処理

▼並列処理

直列処理の実行時間は並列処理の実行時間の3倍以上でした。
複数LLMの回答を比較するだけの簡単な処理でもこれだけの差が生まれるので、複雑な処理になればなるほど、
並列処理の威力は凄まじいものになりそうです。

4. まとめ

Difyの新機能の「パラレル処理(並列処理)」を用いてアプリを作ってみました。
並列処理により効率的にフローが組めるようになったので、うれしいですね。

他にもまだ試せていない並列処理があるので、色々触ってみたいです。
気になる人はぜひ試してみてください。


Acroquest Technologyでは、キャリア採用を行っています。
  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
www.wantedly.com

PythonでPDFからテキスト/表情報の抽出精度を比較してみた

はじめに

こんにちは。ついにジム通いを始めて四六時中筋肉痛を感じながら過ごしているイワツカです。

最近はLLM(大規模言語モデル)とRAG(検索拡張生成)を用いて企業内ドキュメントを活用する取り組みが多く見受けられます。
ドキュメントは基本PDFで保存されているため、PDFからテキストを抽出して、検索対象にすることが必要です。
そこで今回は、PythonでPDFからテキストを抽出するためのライブラリを比較して、どれが良いのか検証しました。

概要

今回はPDF読み取りライブラリとして、PyMuPDF、pdfplumber、unstructuredの3つのPythonライブラリを比較しました。
それぞれのライブラリの特徴は以下の通りです。

ライブラリ 特徴
PyMuPDF 日本語のドキュメントがあり、内容も充実しているので使いやすい。ライセンスはAGPL
pdfplumber 機能がシンプルで扱いやすく、ライセンスがMITなので商用利用しやすい
unstructured Apache-2.0ライセンスで商用利用しやすく、PDF以外にもWordやExcelPowerPointやHTML等、幅広いファイル形式に対応している

まずはPDFからテキスト、表の抽出を行い、最後にLLMやRAGを活用する際に適した形式であるMarkdownに抽出したテキストや表を変換できるか検証しました。

結論としてはpdfplumberが精度も良く扱いやすい印象がありました。

解析対象のPDFは、以下の3つのPDFから見出しや表があるページをピックアップしました。
1. DX白書2023
2. デジタル改革関連法案ワーキンググループ作業部会とりまとめ
3. IT 調達に係る国等の物品等又は役務の調達方針及び調達手続に関する申合せ

具体的なページは以下の4つです。汎用性を確認するため、異なるファイルから選択しています。

サンプル1(テキスト抽出比較用)
サンプル2(テキスト抽出比較用)
サンプル3(表の抽出比較用)
サンプル4(表の抽出比較用)

実装

今回利用した実装はこちらです。
PyMuPDFやpdfplumberでは、PDFファイルを読み込んだ時点でページごとに区切られているので、ページごとの処理を簡単に実装することができます。
unstructuredでは、PDF全体をTitleやListItemというオブジェクトに分解します。そのため、まずはPDFを読み込む関数であるpartition_pdf()の引数のinclude_page_breaksをTrueにしてページの区切りを抽出できるようにし、さらにページごとに処理できるようにする実装が追加で必要でした。

PyMuPDF

import pymupdf

# PDFファイルを読み込む
doc = pymupdf.open(pdf_path)

# テキストを抽出する
num_page = 3
print(doc[num_page].get_text())

# 表を抽出する
num_page = 4
tables = doc[num_page].find_tables()
print(tables[0].extract())

pdfplumber

import pdfplumber

# PDFファイルを読み込む
with pdfplumber.open(pdf_path) as pdf:
    # テキストを抽出する
    num_page = 3
    print(f'{pdf.pages[num_page].extract_text()}')

    # 表を抽出する
    num_page = 4
    tables = pdf.pages[num_page].find_tables()
    print(tables[0].extract())

unstructured

from unstructured.partition.pdf import partition_pdf
from unstructured.documents.elements import PageBreak

def extract_page_elements(page_number):
    """特定のページの要素を抽出する"""
    page_content = []
    current_page = 0

    for element in elements:
        if isinstance(element, PageBreak):
            current_page += 1
        elif current_page == page_number:
            page_content.append(element)

    return page_content

# PDFファイルの読み込み
elements = partition_pdf(
    filename=pdf_path,
    include_page_breaks=True,
    infer_table_structure=True,
    languages=['JPN']
    )

# テキスト抽出
num_page = 3
page = extract_page_elements(num_page)
print("\n".join([str(el) for el in page]))

# 表を抽出する
num_page = 4
page = extract_page_elements(num_page)
tables = [el for el in page if el.category == "Table"]
print(tables[0].metadata.text_as_html)

比較結果

テキスト抽出

まずはテキストがどのように抽出されるのかを見ていきます。

サンプル1のテキスト抽出結果

サンプル1では、一般的によくある見出しと本文の形式のページがどのように抽出されるか比較しました。

PyMuPDF

第2章
国内産業におけるDXの取組状況の概観
マクロ調査
1
(1)
 マクロ的な現状把握調査の概要
現在、
あらゆる産業において、
デジタル技術を用いた既存業務の効率化・高度化や、
新規ビジネス創
出、
ビジネスモデル変革といった、
いわゆるDXの必要性が叫ばれている。
...

pdfplumber

第2章
国内産業におけるDXの取組状況の概観
1 マクロ調査
(1) マクロ的な現状把握調査の概要
現在、あらゆる産業において、デジタル技術を用いた既存業務の効率化・高度化や、新規ビジネス創
出、ビジネスモデル変革といった、いわゆるDXの必要性が叫ばれている。一方で、業界的な慣習や、人
材・スキルの不足、予算上の制約など、さまざまな課題から、DXへの取組が難しい企業もあるものと
推定される。国や調査会社のアンケート調査はDXの取組状況や課題を抽出していると期待される。そ
こで今回、我が国のDXのマクロ的な現状を把握するため、一般に公開されているアンケート結果など
を基に調査を実施した。
本調査のうち、マクロ調査の実施に当たっては、民間企業、官公庁(民間への委託含む)、各種団体にお
...

unstructured

第2章
国内産業におけるDXの取組状況の概観
<oos=
1 マクロ調査
|
(1) マクロ的な現状把握調査の概要
現在、あらゆる産業において、デジタル技術を用いた既存業務の効率化・高度化や、新規ビジネス創 出、ビジネスモデル変革といった、いわゆるDXの必要性が叫ばれている。一方で、業界的な慣習や、人 材・スキ ルの不足、予算上の制約など、さまざまな課題から、DXへの取組が難しい企業もあるものと 推定される。国や調査会社のアンケート調査はDXの取組状況や課題を抽出していると期待される。そ こで今回、我が国のDXのマクロ的な現状を把握するため、一般に公開されているアンケート結果など を基に調査を実施した。
本調査のうち、マクロ調査の実施に当たっては、民間企業、官公庁(民間への委託含む)、各種団体にお けるアンケート調査結果など幅広い資料を収集(図表2-2)し、企業規模、産業、地域ごとのDXの取組の現 状について確認、取りまとめている。また、これらの資料とは別に、IPAが2022年6月から7月にかけて実 施した「企業を中心としたDX推進に関する調査」の結果も一部参照している。
なお、第2部にて参照している各種資料では、その調査ごとにアンケート母集団やアンケート対象者 抽出方法、DXの定義、企業規模・産業・地域などの分類軸が異なる。そのため、各調査結果が示す傾向 は必ずしも整合するものではないが、DX事例収集における整理軸としての企業属性と、その属性別で の大まかな傾向を見極めるためには有効と考えている。
042 DX白書2023
  • PyMuPDF:PDF上での改行に加えて、句読点や単語の途中にも改行が入る
  • pdfplumber:PDFの見た目通りに抽出されている
  • unstructured:PDF上での改行は基本的に半角空白として抽出される。段落の変わり目で改行が入る

サンプル2のテキスト抽出結果

サンプル2では、論文等で多い1ページ内に複数の列がある場合、という観点で比較してみます。

PyMuPDF

- 5 - 
 
別紙1 対象とする政府機関等 
 
【国の行政機関】 
内閣官房 
内閣法制局 
人事院 
...

pdfplumber

別紙1 対象とする政府機関等
【国の行政機関】 国立美術館 産業技術総合研究所
内閣官房 国立文化財機構 製品評価技術基盤機構
内閣法制局 教職員支援機構 新エネルギー・産業技術総合
...

unstructured

別紙1 対象とする政府機関等
【国の行政機関】 内閣官房 内閣法制局 人事院 内閣府 宮内庁 公正取引委員会 個人情報保護委員会 カジノ管理委員会 警察庁 金融庁 消費者庁 復興庁 総務省 法務省 外務省 財務省 文部科学省 厚生労働省 農林水産省 経済産業省 国土交通省 環境省 防衛省 会計検査院 【独立行政法人】 国立公文書館 北方領土問題対策協会 日本医療研究開発機構 国民生活センター 情報通信研究機構 統計センター 郵便貯金簡易生命保険管理・ 郵便局ネットワーク支援機 構 国際協力機構 国際交流基金 酒類総合研究所 造幣局 国立印刷局 国立特別支援教育総合研究 所
大学入試センター 国立青少年教育振興機構 国立女性教育会館 国立科学博物館 物質・材料研究機構 防災科学技術研究所 量子科学技術研究開発機構
国立美術館 国立文化財機構 教職員支援機構 科学技術振興機構 日本学術振興会 理化学研究所 宇宙航空研究開発機構 日本スポーツ振興センター 日本芸術文化振興会 日本学生支援機構 海洋研究開発機構 国立高等専門学校機構 大学改革支援・学位授与機構 日本原子力研究開発機構 勤労者退職金共済機構 高齢・障害・求職者雇用支援 機構 福祉医療機構 国立重度知的障害者総合施 設のぞみの園 労働政策研究・研修機構 労 働者健康安全機構 国立病院機構 医薬品医療機器総合機構 医薬基盤・健康・栄養研究所 地域医療機能推進機構 年金積立金管理運用独立行 政法人 国立がん研究センター 国立循環器病研究センター 国立精神・神経医療研究セン ター 国立国際医療研究センター 国立成育医療研究センター 国立長寿医療研究センター 農林水産消費安全技術セン ター 家畜改良センター 農業・食品産業技術総合研究 機構 国際農林水産業研究セ ンタ ー
森林研究・整備機構 水産研究・教育機構 農畜産業振興機構 農業者年金基金 農林漁業信用基金 経済産業研究所 工業所有権情報・研修館
- 5 -
産業技術総合研究所 製品評価技術基盤機構 新エネルギー・産業技術総合
開発機構 日本貿易振興機構 情報処理推進機構 石油天然ガス・金属鉱物資源 機構 中小企業基盤整備機構 土木研究所 建築研究所 海上・港湾・航空技術研究所 海技教育機構 航空大学校 自動車技術総合機構 鉄道 建設・運輸施設整備支援 機構 国際観光振興機構 水資源機構 自動車事故対策機構 空港周辺整備機構 都市再生機構 奄美群島振興開発基金 日本高速道路保有・債務返済 機構 住宅金融支援機構 国立環境研究所 環 境再生保全機構 駐留軍等労働者労務管理機 構 【サイバーセキュリティ基 本法に定める指定法人】 地方公共団体情報システム 機構 地方公務員共済組合連合会 地方職員共済組合 都職員共済組合 全国市町村職員 共済組合連 合会 国家公務員共済組合連合会 日本私学学校振興・共済事業 団 公立学校共済組合 日本年金機構
  • PyMuPDF:PDF上で3列あるが、左の列から順に1列ずつ分けて抽出できている
  • pdfplumber:横方向に1行として読み込んでいて、別の列のテキストが半角空白で繋がって抽出されている
  • unstructured:3列を分けて抽出できているが、基本的に改行は半角空白として抽出されている

表の抽出

次に表を抽出できるかどうかを見ていきます。

サンプル3の表抽出結果

サンプル3では、一般的によくある形式の表を抽出できるか比較しました。

PyMuPDF

[['分類', 'No.', '発行元', '題名'],
 ['民間\n企業', '1', '株式会社NTTデータ経営研究所', '日本企業のデジタル化への取り組みに関するアンケート調査\n(2019年8月)'],
 [None, '2', '株式会社帝国データバンク', 'DX推進に関する企業の意識調査(2022年1月)'],
...

pdfplumber

[['分類', 'No.', '発行元', '題名'],
 ['民間\n企業', '1', '株式会社NTTデータ経営研究所', '日本企業のデジタル化への取り組みに関するアンケート調査\n(2019年8月)'],
 [None, '2', '株式会社帝国データバンク', 'DX推進に関する企業の意識調査(2022年1月)'],
...

unstructured

<table><thead><tr><th>切 類</th><th>| No.</th><th>発 行 元</th><th>顕 名</th></tr></thead><tbody><tr><td rowspan="11">民 間 丁 苫</td><td>①</td><td>| 株 式 会 社 NTT デ ー タ 経 営 研 究 所</td><td>日本止萱のデシタル化への取り組みに関するアンケ一 ト 調 査 (②0①⑨ 年 ⑧ 月 )</td></tr><tr><td>②</td><td>| 株 式 会 社 帖 国 デ ー タ バ ン ク</td><td>DX 推 進 に 関 す る 企 業 の 意 講 調 査 (②0②② 年 ① 月 )</td></tr><tr><td>ク ①</td><td>ー っ ー タ ル バ | 株 式 会 社 帝 国 デ ー タ バ ン ク</td><td>特 別 企 画 : DX 推 進 に 関 す る 企 業 の 実 態 ( `DX 推 進 に 関 す る 企 業 の 意 識 調 査 」 デ ー タ か ら の 分 析 含 む ) (②0②② 年 ③ 月 )</td></tr><tr><td>③</td><td>| PwC Japan グ ル ー プ</td><td>⑳②① 年 DX 意 識 調 査 一 T モ ダ ナ イ ゼ ー シ ョ ン 編 一</td></tr><tr><td>④</td><td>| 株 式 会 社 INDUSTRIAL-X</td><td>企 業 の DX 実 現 に 向 け た 課 題 と コ ロ ナ 前 後 の 意 向 に 関 す る 調 査 (②0②① 年 ⑦ 月 )</td></tr><tr><td>⑤</td><td>| MM 総 研</td><td>中 小 企 業 の DX 推 進 に お け る 課 題 分 析 (②0②② 年 ③ 月 )</td></tr><tr><td>⑥</td><td>| 株 式 会 社 エ イ ト レ ッ ド</td><td>地 方 都 市 の 中 小 企 業 の DX 実 態 調 査 (②0②② 年 ① 月 )</td></tr><tr><td>⑦</td><td>| 株 式 会 社 エ イ ト レ ッ ド</td><td>東 京 都 の 中 小 企 業 に お け る DX 実 態 調 査 (②0②① 年 ⑫ 月 )</td></tr><tr><td>⑧</td><td>a ッ | 株 式 会 社 電 通 デ ジ タ ル</td><td>日 本 に お け る 企 業 の デ ジ タ ル ト ラ ン ス フ ォ ー メ ー シ ョ ン 調 査 (②0②① 年 度 )</td></tr><tr><td>⑨</td><td>| ア ビ ー ム コ ン サ ル テ ィ ン グ 株 式 会 社</td><td>日 本 企 業 の DX 取 り 組 み 実 態 調 査 (②0②0 年 ⑫ 月 )</td></tr><tr><td>①0</td><td>| デ ル ・ テ ク ノ ロ ジ ー ズ 株 式 会 社</td><td>DX 動 向 調 査 ⑳②①</td></tr><tr><td rowspan="7">言 六 庁</td><td>⑪</td><td>| 経 済 産 業 省</td><td>デ ジ タ ル ト ラ ン ス フ ォ ー メ ー シ ョ ン 調 査 ⑳②②</td></tr><tr><td>⑫</td><td>活 産 蒋 ① | 綱 淡 産 業 貫</td><td>地 域 末 来 牽 引 企 業 ア ン ケ ー ト ( 第 ② 回 ス マ ー ト か つ 強 靱 な 地 域 経 済 社 会 の 実 現 に 向 け た 研 究 会 資 料 ②) ( 令 和 ③ 年 ② 月 )</td></tr><tr><td>①③</td><td>| 経 済 産 業 省</td><td>令 和 ② 年 度 中 小 企 業 の デ ジ タ ル 化 に 関 す る 調 査</td></tr><tr><td>| ⑭</td><td>閻 闇 継 ; | 経 済 産 業 省 中 国 経 済 産 業 局</td><td>中 国 地 域 に お け る 地 域 末 来 牽 引 企 業 等 の 経  営 デ ジ タ ル 化 ・DX の 太 愛 醇 査 ー</td></tr><tr><td>⑮</td><td>| 誌 稗 着 / 情 報 通 信 総 合 研 究 所</td><td>デ ジ タ ル デ ー タ の 細 沼 め 任 体 の M 湯 と 江 服 の 理 仁 に 眺 す る 顆 ま</td></tr><tr><td>⑯</td><td>o ル ル eame m② ぷ ー | 総 務 責 情 報 通 信 総 合 研 究 所</td><td>デ ジ タ ル ・ ト ラ ン ス フ ォ ー メ ー シ ョ ン に よ る 経 済 へ の イ ン バ ク ト に 関 す る 調 査 研 究 (②0②① 年 ③ 月 )</td></tr><tr><td>⑰</td><td>| 文 部 科 学 省 科 学 技 術 ・ 学 術 政 簗 研 究 所</td><td>| 科 学 技 術 に 関 す る 国 民 意 講 調 査 ーDX に つ い て 一</td></tr><tr><td rowspan="6">て の f 団 体</td><td>①⑧</td><td>ー ク m u | 独 立 行 政 法 人 情 報 処 理 推 進 機 構</td><td>デ ジ タ ル ・ ト ラ ン ス フ ォ ー メ ー シ ョ ン (DX) 推 進 に 向 け た  企 業 と IT 人 材 の 実 態 調 査 (②0②0 年 ⑤ 月 ⑭ 日 )</td></tr><tr><td>⑲</td><td>| 独 立 行 政 法 人 情 報 処 理 推 進 機 様</td><td>デ ジ タ ル 時 代 の ス キ ル 変 革 等 に 関 す る 調 査 (②0②② 年 ④ 月 )</td></tr><tr><td>⑳</td><td>| 独 立 行 政 法 人 情 報 処 理 推 進 機 構</td><td>デ ジ タ ル 時 代 の ス キ ル 変 革 等 に 関 す る 調 査 (②0②① 年 ④ 月 )</td></tr><tr><td>①</td><td>| 狗 工 行 攻 沼 人 小 な 業 基 槌 整 侵 槻 機</td><td>| 中 小 会 業 の DX 推 進 に 関 す る 調 査 (②0②② 年 ⑤ 月</td></tr><tr><td>②②</td><td>独 立 行 政 法 人 労 働 政 篠 研 究 ・ 研 修 機 構</td><td>も の づ く り 産 業 に お け る DX ( デ ジ タ ル ト ラ ン ス フ ォ ー メ ー シ ョ ン ) に 対 応 し た 人 材 の 確 保 ・ 育 成 や 働 き 方 に 関 す る 調 査 ( 令 和 ③ 年 ⑤ 月 )</td></tr><tr><td>②③</td><td>日 本 情 報 シ ス テ ム ・ ユ ー ザ ー 協 会</td><td>企 業 T 動 向 調 査 報 告 書 ⑳②②</td></tr></tbody></table>
  • PyMuPDF:表の構造をリスト形式で抽出できている
  • pdfplumber:表の構造をリスト形式で抽出できている
  • unstructured:表の構造をHTML形式で抽出できているが、文字の読み取り精度は低い

サンプル4の表抽出結果

サンプル4では列数が多く、空白セルも混ざるようなケースを見てみました。
結果はサンプル3と同様の結果となりました。

PyMuPDF

[['大分類',
  '小分類',
  'A. 整備方針\nの策定',
  'B. デジタル庁の\n統括・監理',
  'C. 個別システム\nの整備・運用',
  'D. 一括予算\n計上(注)',
  '備考'],
 ['(1)国のシステム',
  '①デジタル庁システム',
  '○',
  '○',
  'デジタル庁が\n整備・運用',
  '○',
  '国、独法、地方公共団体、準公共の民間事\n業者が相互に連携するためのシステムを含む'],
...

pdfplumber

[['大分類',
  '小分類',
  'A. 整備方針\nの策定',
  'B. デジタル庁の\n統括・監理',
  'C. 個別システム\nの整備・運用',
  'D. 一括予算\n計上(注)',
  '備考'],
 ['(1)国のシステム',
  '①デジタル庁システム',
  '○',
  '○',
  'デジタル庁が\n整備・運用',
  '○',
  '国、独法、地方公共団体、準公共の民間事\n業者が相互に連携するためのシステムを含む'],
...

unstructured

<table><thead><tr><th>, 大 分 願</th><th>, 願</th><th>整 備 方 針 ん 荒 俊 f</th><th>|B. デ ジ B デ シ ク ル To</th><th>. 個 別 C シス テ ム</th><th>・ 一 括 予 算 D 一 拳 争</th><th>側 老</th></tr></thead><tbody><tr><td rowspan="3">- o</td><td>ー ム ー っ ー デ シ タ シ ム</td><td>り</td><td>り</td><td>デ ジ タ ル 庁 が 整 備 ・ 運</td><td></td><td>国 、 独 法 、 地 方 公 共 団 体  、 準 公 共 の 民 間 事 業 者 が 相 互 に 連 携 す る た め の シ ス テ ム を 含 む</td></tr><tr><td>② デ ジ タ ル 庁 . 各 府 省 共 同 プ ロ ジェ ク | 埋 楊</td><td>ー</td><td>ド ー</td><td>デ ジ タ ル 庁 が 整 備 談 託</td><td>| ー</td><td></td></tr><tr><td>⑨ 名 府 省 シ ス テ ム</td><td>ら</td><td>O。</td><td>啓 体 増 健</td><td>“</td><td>が R ④ 年 度 以 陳 の 取 扱 い よ 一 拭 計 上 の</td></tr><tr><td rowspan="2">つ 猛 法 の シ ス テ (②) 独 法 の シ ス テ ム</td><td>〟 - 人</td><td>-</td><td>0</td><td>鯛 き の 宇 体 漸 健 - 運</td><td>轟</td><td>誌 醇 バ レ ク の 付 の 場 ※ 運 営 買 交 付 金 以 外 の 交 付 金 の 場 合 は 、 ー 括 計 上 可 能 か</td></tr><tr><td>, る @ 上 記 以 外 の シ ス テ ム</td><td>ら</td><td>限 ( デ ジ タ ル 庁 が 指 導 ・ 助 言 )</td><td>ー | ( デ ジ タ ル 庁 が | 指 導 ・ 助 言 )</td><td></td><td>独 法 が 整 備 - 運 用</td></tr><tr><td rowspan="2">(③) 地 方 公 共 団 体 の シ ス テ ム</td><td>① 国 の 補 助 金 が 交 付 さ れ る シ ス テ ム</td><td>ら ン (※) ~</td><td>〇</td><td>各 府 省 が 祷 助 金 執 行 地 方 が 整 備 . 運</td><td></td><td>※ 標 準 化 法 の 基 本 方 針 は 、 総 務 省 と 共 同 で 策 定</td></tr><tr><td>る @ 上 記 以 外 の シ ス テ ム</td><td>ら</td><td>x</td><td>x</td><td></td><td>地 方 が 整 備 ・ 運 用</td></tr><tr><td rowspan="2">( の 森 公 公 野 の 氏 間 事 業 者 の シ ス テ ム</td><td>g 急 に 整 零 の シ ス テ ① 緊 急 に 整 備 を 要 す る 等 の シ ス テ ム |</td><td>ら x 囲 安 と 共 同 )</td><td>⑤ 談</td><td>デ ジ タ ル 庁 ・ 各 府 ② 浩 閻 m i</td><td></td><td>&gt; リ ー ※ 整 備 の 緊 怠 性 の 度 合 い 等 に 応 じ 、 様 々 な 整 eect</td></tr><tr><td>| る @ 上 記 以 外 の シ ス テ ム</td><td>O C 栄 畜 D</td><td>x</td><td></td><td></td><td>ョ 事 業 者 が 整 備 - 運 用</td></tr><tr><td rowspan="2">な な 民 間 事 業 者 の シ ス テ ム</td><td>① 相 互 連 携 分 野 の 民 間 事 業 者 が 利 す る シ ス テ ム</td><td>A ( 標 準 を 策 定 )</td><td>國</td><td></td><td></td><td>。 事 業 者 が 整 備 . 運 用</td></tr><tr><td>② 上 記 以 外 の シ ス テ ム</td><td></td><td></td><td></td><td></td><td>事 業 者 が 整 備 . 運 用</td></tr></tbody></table>

検証結果

実際に検証した所感に、ライセンスも加えて結果をまとめると以下のようになります。

PyMuPDF pdfplumber unstructured
テキスト抽出
句読点や単語の途中でも改行が入る

PDFの見た目通りに抽出可能

段落等の大きな変化がある所のみ改行
表抽出
リスト形式で抽出可能

リスト形式で抽出可能

HTML形式で抽出可能 読取精度は低い
ライセンス AGPL MIT license Apache-2.0

unstructuredの表の読み取り精度が低いのは、テキスト抽出と異なり、構造を読み取るためにOCRベースのモデルに読取方法が変更されるためだと思われます。

また、RAGであればMarkdown化することで精度が上がることがありますが、今回比較したライブラリではMarkdown化までは対応してませんでした。

まとめ

今回はPDFからテキストや表を抽出可能なPythonライブラリとして、PyMuPDF、pdfplumber、unstructuredの3つを比較しました。
個人的には、シンプルな実装でライセンス的にも制約が少ないpdfplumberが一番扱いやすい印象を受けました。
表に関しても適切に抽出でき、構造化されていた点が良かったです。
実装を追加すれば抽出した文字の大きさを取得でき、文字の大きさから見出しかどうか等の判定も可能になると思われるので、機会があれば試してみたいと思います。

Acroquest Technologyでは、キャリア採用を行っています。
  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
  少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com