約6万件の日本語テキストをベクトル化をGPUとCPU処理で比較してみた

約6万件の日本語テキストをベクトル化をGPUとCPU処理で比較してみた

約6万件の日本語テキストを対象に、多言語モデル(multilingual-e5-large)と日本語特化モデル(ruri-base)のベクトル化性能をGPU/CPU環境で実測比較。前者はGPUの採用で15倍の高速化を確認した一方、コスト効率と実用性のバランスから、CPU処理でも十分な性能を発揮した「ruri-base」の採用に至った検証結果を紹介します。
2026.02.03

約6万件の日本語テキストのベクトル化を試みる機会がありました。

今回、2つの日本語対応モデル(multilingual-e5-large と ruri-base)を使い、CPU(c7i.xlarge)とGPU(g4dn.xlarge)のEC2インスタンス上でDocker環境を構築、その性能を比較した結果を紹介します。

実行環境とデータ

検証に使用したリソースおよび入力データは以下の通りです。

インスタンス構成

GPU環境:

  • インスタンス: g4dn.xlarge (4 vCPU / 16 GiB / カスタム Intel Cascade Lake / NVIDIA Tesla T4)
  • AMI: Deep Learning OSS Nvidia Driver AMI GPU PyTorch 2.9 (Amazon Linux 2023) 20260128 (ami-00cef3bb12d4413ab)

CPU環境:

  • インスタンス: c7i.xlarge (4 vCPU / 8 GiB / 第4世代 Intel Xeon Scalable - Sapphire Rapids)
  • AMI: Amazon Linux 2023 AMI 2023.10.20260120.4 (ami-055a9df0c8c9f681c)

入力データ

  • 内容: 記事要約JSON(タイトル、要約、ID)
  • レコード数: 59,768 件
  • ファイル容量: 約32.0 MB
  • 平均サイズ: 約560 bytes / 件

性能比較結果

multilingual-e5-large モデル

多言語対応の高性能モデル(1024次元、391Mパラメータ)での比較結果:

項目 CPU (c7i.xlarge) GPU (g4dn.xlarge) 比較結果
処理時間 約7時間 (推定) 27分51秒 約15倍 高速
スループット 143 記事/分 2,145 記事/分 -
コスト $1.25 (7h × $0.178) $0.24 (0.46h × $0.526) 81% 削減
  • CPU版は1,000記事処理時点でのスループット(143記事/分)から全体の処理時間を推定
  • 単価: c7i.xlarge $0.178/h、g4dn.xlarge $0.526/h(us-west-2 オンデマンド)

ruri-base モデル

日本語特化の軽量モデル(768次元、199Mパラメータ)での比較結果:

項目 CPU (c7i.xlarge) GPU (g4dn.xlarge) 比較結果
処理時間 38分19秒 21分43秒 1.76倍 高速
スループット 1,560 記事/分 2,752 記事/分 -
コスト $0.11 (0.64h × $0.178) $0.19 (0.36h × $0.526) 73% 増加

コストと所要時間の比較

モデル 環境 処理時間 コスト
ruri-base CPU (c7i.xlarge) 38分19秒 $0.11
ruri-base GPU (g4dn.xlarge) 21分43秒 $0.19
multilingual-e5-large CPU (c7i.xlarge) 約7時間 $1.25
multilingual-e5-large GPU (g4dn.xlarge) 27分51秒 $0.24

参考: モデルロード時間

  • ruri-base: 6.8秒
  • multilingual-e5-large: 58.4秒(約8.6倍の差)

※Dockerのキャッシュを利用して、メモリへの展開時間のみ計測しました(CPU環境)。処理時間には含まれていません。

まとめ

今回の検証により、利用するモデルに合わせた適切なインスタンスの選定の重要性が確認できました。

当面は費用対効果を優先し、通常のFargateなどの実行環境での利用を予定しているため、CPU処理で実用的な性能を発揮できた ruri-base をベクトル化のモデルとして採用予定です。

今後の多言語対応や、よりベクトル精度の高いモデルとして multilingual-e5-large を利用することになった場合、GPU対応インスタンスや実行環境を活用したいと思います。


参考情報

今回の検証に利用したDockerとPythonコードです。

AWS公式が提供するDeep Learning AMI(Nvidia Driver版)を利用することで、特別なドライバ設定やCUDAのインストール作業なしに、Docker環境でGPUを活用できました。--gpus all フラグを指定するだけで、コンテナ内からGPUリソースにアクセス可能です。

また、sentence-transformers ライブラリはGPU/CPUを自動判定するため、同一のPythonコードでCPU環境とGPU環境の両方で動作します。環境の違いはDockerfileのベースイメージと実行時の --gpus フラグのみで吸収できるため、開発・検証がスムーズに行えました。

multilingual-e5-large の実装

Dockerfile の比較

GPU版 (g4dn.xlarge用):

# CUDA対応のPyTorchイメージ
FROM pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    TZ=Asia/Tokyo

WORKDIR /app

RUN pip install --no-cache-dir sentence-transformers boto3

COPY vectorize-simple.py .

CMD ["python", "-u", "vectorize-simple.py"]

CPU版 (c7i.xlarge用):

# CPU専用のPyTorchイメージ
FROM pytorch/pytorch:2.5.1-cpu

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    TZ=Asia/Tokyo

WORKDIR /app

RUN pip install --no-cache-dir sentence-transformers boto3

COPY vectorize-simple.py .

CMD ["python", "-u", "vectorize-simple.py"]

: 今回の検証ではCUDAイメージを使用しましたが、CPU専用イメージ(pytorch:2.5.1-cpu)を使用することで、Intel MKLなどのCPU最適化により、さらに性能が向上する可能性があります。

Pythonコード (CPU/GPU共通)

sentence-transformers はCUDAを自動検出するため、コード側での特別な処理は不要でした。

import json
import os
from datetime import datetime
from sentence_transformers import SentenceTransformer
import boto3

# 環境変数から設定を取得
bucket = os.environ['S3_BUCKET']
input_key = os.environ['INPUT_KEY']
output_key = os.environ['OUTPUT_KEY']

s3 = boto3.client('s3')

# S3から入力ファイルをダウンロード
print('Downloading input file...')
s3.download_file(bucket, input_key, '/tmp/articles_ja.json')

# モデルロード(GPUがあれば自動的に使用される)
model_name = os.environ.get('MODEL_NAME', 'intfloat/multilingual-e5-large')
print(f'Loading model: {model_name}...')
model = SentenceTransformer(model_name)

# 記事データ読み込み
print('Loading articles...')
with open('/tmp/articles_ja.json', 'r') as f:
    articles = json.load(f)

print(f'Processing {len(articles)} articles...')

# ベクトル化処理(1件ずつ処理、100件ごとに進捗表示)
with open('/tmp/vectors_ja.jsonl', 'w') as f:
    for i, article in enumerate(articles):
        text = f"{article['title']} {article['summary']}"
        vector = model.encode(text).tolist()
        result = {
            'article_id': article['id'],
            'vector': vector,
            'dimension': len(vector),
            'model': model_name,
            'timestamp': datetime.utcnow().isoformat()
        }
        f.write(json.dumps(result, ensure_ascii=False) + '\n')
        if (i + 1) % 100 == 0:
            print(f'Processed {i + 1}/{len(articles)} articles')

# S3にアップロード
print('Uploading results...')
s3.upload_file('/tmp/vectors_ja.jsonl', bucket, output_key)
print('Done!')

ポイント:

  • SentenceTransformer が自動的にGPU/CPUを判定
  • デバイス指定コード不要
  • 同じコードでCPU/GPU両環境で動作
  • 100件ごとに進捗ログを出力してスループット測定を可能に

実行コマンド

GPU版 (g4dn.xlarge):

docker run --gpus all --rm \
  -e S3_BUCKET=your-bucket-name \
  -e INPUT_KEY=articles_ja.json \
  -e OUTPUT_KEY=vectors_ja_gpu_${TIMESTAMP}.jsonl \
  vectorize-gpu

CPU版 (c7i.xlarge):

docker run --rm \
  -e S3_BUCKET=your-bucket-name \
  -e INPUT_KEY=articles_ja.json \
  -e OUTPUT_KEY=vectors_ja_cpu_${TIMESTAMP}.jsonl \
  vectorize-cpu

差分: GPU版のみ --gpus all フラグが必要です。

ruri-base の実装

日本語特化モデル ruri-base を使用する場合、日本語形態素解析のための追加パッケージが必要でした。

Dockerfile

GPU版:

FROM pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime

# 日本語形態素解析用パッケージを追加
RUN pip install --no-cache-dir sentence-transformers boto3 protobuf fugashi unidic-lite

COPY vectorize-ruri-base.py /app/vectorize-ruri-base.py

WORKDIR /app

CMD ["python", "vectorize-ruri-base.py"]

CPU版:

FROM pytorch/pytorch:2.5.1-cuda11.8-cudnn9-runtime

# 日本語形態素解析用パッケージを追加
RUN pip install --no-cache-dir sentence-transformers boto3 protobuf fugashi unidic-lite

COPY vectorize-ruri-base.py /app/vectorize-ruri-base.py

WORKDIR /app

CMD ["python", "vectorize-ruri-base.py"]

追加パッケージ:

  • protobuf: モデル設定ファイルの読み込み
  • fugashi: 日本語形態素解析器(MeCabのPythonバインディング)
  • unidic-lite: 日本語辞書(軽量版、約45MB)

Pythonコード (CPU/GPU共通)

import json
import os
from datetime import datetime
from sentence_transformers import SentenceTransformer
import boto3

bucket = os.environ['S3_BUCKET']
input_key = os.environ['INPUT_KEY']
output_key = os.environ['OUTPUT_KEY']

s3 = boto3.client('s3')

print('Downloading input file...')
s3.download_file(bucket, input_key, '/tmp/articles_ja.json')

print('Loading model: cl-nagoya/ruri-base...')
model = SentenceTransformer('cl-nagoya/ruri-base')

print('Loading articles...')
with open('/tmp/articles_ja.json', 'r') as f:
    articles = json.load(f)

print(f'Processing {len(articles)} articles...')

with open('/tmp/vectors_ja.jsonl', 'w') as f:
    for i, article in enumerate(articles):
        # ruriモデルは "文章: " プレフィックスを推奨
        text = f"文章: {article['title']} {article['summary']}"
        vector = model.encode(text).tolist()
        result = {
            'article_id': article['id'],
            'vector': vector,
            'dimension': len(vector),
            'model': 'cl-nagoya/ruri-base',
            'timestamp': datetime.utcnow().isoformat()
        }
        f.write(json.dumps(result, ensure_ascii=False) + '\n')
        if (i + 1) % 100 == 0:
            print(f'Processed {i + 1}/{len(articles)} articles')

print('Uploading results...')
s3.upload_file('/tmp/vectors_ja.jsonl', bucket, output_key)
print('Done!')

ポイント:

  • ruriモデルは入力テキストに "文章: " プレフィックスを付けることを推奨
  • それ以外はmultilingual-e5-largeと同じ構造

実行コマンド

GPU版:

docker run --gpus all --rm \
  -e S3_BUCKET=your-bucket-name \
  -e INPUT_KEY=articles_ja.json \
  -e OUTPUT_KEY=vectors_ja_ruri_gpu_${TIMESTAMP}.jsonl \
  vectorize-ruri-gpu

CPU版:

docker run --rm \
  -e S3_BUCKET=your-bucket-name \
  -e INPUT_KEY=articles_ja.json \
  -e OUTPUT_KEY=vectors_ja_ruri_cpu_${TIMESTAMP}.jsonl \
  vectorize-ruri-cpu

この記事をシェアする

FacebookHatena blogX

関連記事