はじめに
こんな課題、ありませんか?
- BigQueryでテキスト解析をしたいが、N-gramだとトークン数が爆発して計算コストが高すぎる…
- 形態素解析(MeCabやSudachi)を使いたいが、Remote Functions(Cloud Functions)の構築や、辞書・ライブラリのGCS配置が面倒で手が出せない…
- 厳密な解析精度までは求めていないから、とにかく手軽に、それなりの精度でキーワードを抽出したい…
この記事では、そんな悩みを解決する「BigQueryのSQL(UDF)だけで完結する『形態素解析もどき』の軽量日本語トークナイザー」をご紹介します。
外部のAPIやGCSへのファイル配置、複雑な環境構築は一切不要。
SQLエディタにコードをコピペするだけで、永続的な関数としても、使い捨ての TEMP FUNCTION としても、すぐに使い始められます。
自己紹介
株式会社ケンサク代表の堀田と申します。「良いものを、見つけやすく」をミッションに、機械学習を活用した検索・レコメンデーション改善の技術開発に取り組んでいます。特にECサイトやメディアサイトでの商品・コンテンツ発見体験の向上を専門としており、SQLベースの軽量な手法から最新のEmbedding技術まで幅広くカバーしています。
なぜ「トークナイズ」で悩むのか?
BigQueryで日本語テキストを扱う際、本来やりたいのは「テキストの類似度計算」や「キーワード抽出」です。
しかし、その前段階である「単語分割(トークナイズ)」の時点で、コストや手間の壁にぶつかりがちです。
厳密な「形態素解析」は、単なるトークナイズだけでなく、品詞分解や活用形の正規化まで行いますが、多くの用途(特に類似度計算など)では、そこまでの高機能さは不要で、単に「いい感じに単語が分かれていること」さえ満たせれば十分なケースも多いです。
現状の主な選択肢は以下の2つですが、どちらも帯に短し襷に長しです。
1. N-gram(文字単位での分割)
前回の記事「【SQLだけで完結】BigQueryで類似アイテム推薦を「安く、速く」実装する方法」でも紹介した手法です。
-
メリット: 実装が非常に簡単(
ML.NGRAMS関数など)。 - デメリット: 「東京都」→「東京」「京都」のように分割されるため、トークン数が多くなりがち。計算量が増え、類似度計算などのコストが跳ね上がる原因になります。また、意味のない語(ノイズ)も大量に生成されます。
2. 形態素解析(MeCab, Sudachiなど)
- メリット: 辞書に基づいて正確に単語を分割できる。「東京都」→「東京都」として扱える。
-
デメリット: BigQueryで動かすには、以下のいずれかの手間が発生します。
- Remote Functions: Cloud Functionsを別途デプロイ・管理する必要があり、通信オーバーヘッドも発生する。
- GCSへのライブラリ配置: TinySegmenterなどのJSライブラリや辞書ファイルをGCSにアップロードし、UDFから読み込む設定が必要。
- WASM: 高速だが、ビルド環境の準備やバイナリの配置など、実装コストが高い。
提案手法:「文字種」に着目した「形態素解析もどき」
そこで今回は、「厳密な形態素解析ではないが、実用上十分な精度が出る」 第3の選択肢を提案します。
アイデアは単純です。
「日本語の文章において、意味を持つ単語(名詞など)は『漢字』や『カタカナ』で書かれていることが多い」 という特徴を利用します。
戦略
- 正規化: 全角英数字を半角に、大文字を小文字に統一します。
- 区切り文字の選定: 漢字・カタカナ・アルファベット以外の文字(主に「ひらがな」や記号)を、単語の区切り(スペース)として扱います。
- 英数字の分離: 「iPhone15」のように英字と数字が連続する場合も分割しやすいように調整します(今回は簡易化のため、アルファベット境界のみ処理)。
これにより、「助詞」や「助動詞」などのひらがな部分が自然とフィルタリングされ、実質的にストップワード除去を兼ねたトークナイズが可能になります。
実装:JavaScript UDF K_TOKENIZE
実際にBigQueryで使用できるUDF(ユーザー定義関数)のコードです。
CREATE OR REPLACE FUNCTION `YOUR_PROJECT_ID`.`YOUR_DATASET_ID`.K_TOKENIZE(input STRING)
RETURNS ARRAY<STRING>
LANGUAGE js AS """
if (!input) return [];
// 全角文字を半角に変換 (例: 全角英数字 → 半角英数字)
let normalized = input.normalize('NFKC');
// すべての文字を小文字に変換
normalized = normalized.toLowerCase();
// 漢字、カタカナ、アルファベット以外をスペースに置換
// \\u4E00-\\u9FFF: 漢字
// \\u30A0-\\u30FF: カタカナ
let result = normalized.replace(/[^\\u4E00-\\u9FFF\\u30A0-\\u30FFa-zA-Z]+/g, ' ');
// 特定の記号(例: ・)をスペースに置換(カタカナ範囲に含まれる場合などの調整)
result = result.replace(/[・]/g, ' ');
// アルファベットとそれ以外の境界にスペースを挿入
result = result.replace(/([a-z])([^a-z])/g, '$1 $2');
result = result.replace(/([^a-z])([a-z])/g, '$1 $2');
// 複数のスペースを1つのスペースに圧縮し、前後の空白を除去
result = result.replace(/\\s+/g, ' ').trim();
// 空文字列の場合は空配列を返す
if (result === '') return [];
// スペースで分割して配列にする
return result.split(' ');
""";
このSQLを実行して関数を定義しておけば、あとは SELECT K_TOKENIZE(contents) FROM ... のように呼び出すだけで使えます。
今すぐ試せるコピペ用SQL
「まずは手元のデータで試してみたい」という方は、以下のSQLをBigQueryコンソールに貼り付けて実行してみてください。TEMP FUNCTION として定義しているので、プロジェクトIDなどの書き換えなしでそのまま動きます。
-- 一時関数として定義(このクエリ内でのみ有効)
CREATE TEMP FUNCTION TEMP_K_TOKENIZE(input STRING)
RETURNS ARRAY<STRING>
LANGUAGE js AS """
if (!input) return [];
let normalized = input.normalize('NFKC').toLowerCase();
let result = normalized.replace(/[^\\u4E00-\\u9FFF\\u30A0-\\u30FFa-zA-Z]+/g, ' ');
result = result.replace(/[・]/g, ' ');
result = result.replace(/([a-z])([^a-z])/g, '$1 $2');
result = result.replace(/([^a-z])([a-z])/g, '$1 $2');
result = result.replace(/\\s+/g, ' ').trim();
if (result === '') return [];
return result.split(' ');
""";
-- サンプルデータで実行確認
WITH sample_data AS (
SELECT "私は株式会社ケンサクの代表です。機械学習で検索エンジンの改善をしています。" AS text
UNION ALL
SELECT "美味しいお寿司を食べた"
)
SELECT
text,
TEMP_K_TOKENIZE(text) AS tokenized
FROM
sample_data;
実行結果と評価
サンプル実行
上記のSQLを実行すると、以下のような結果が得られます。
入力テキスト:
「私は株式会社ケンサクの代表です。機械学習で検索エンジンの改善をしています。」
「美味しいお寿司を食べた」
K_TOKENIZEの結果:
["私", "株式会社ケンサク", "代表", "機械学習", "検索エンジン", "改善"]
["美味", "寿司", "食"]
解説:
- 「は」「の」「です」「で」「を」「しています」といった、ひらがな中心の助詞・助動詞部分がきれいに消えています。
- 「美味しい」の「しい」や「食べた」の「べた」といった送り仮名も消えていますが、「美味」「食」といった意味の根幹部分は残るため、類似度計算には十分使えます。
- 「株式会社ケンサク」「機械学習」といった複合語が、ひとかたまりのトークンとして抽出されています。これは2-gramでは得られないメリットです。
- ※「私」のような漢字1文字の語は残りますが、必要に応じてストップワードとして除外処理を追加することも容易です。
この手法のメリット・デメリット
メリット:
- コピペ一発で導入完了: 複雑な環境構築やファイル配置は一切不要。SQLエディタに貼り付けるだけで、今すぐTEMP FUNCTIONとしても使えます。
- 圧倒的に軽い: 外部辞書の読み込みがなく、単純な文字列置換のみなので高速です。
- トークン数が激減: N-gramと比較してトークン数が大幅に減るため、後段の類似度計算(JOIN処理)のコストが劇的に下がります。
- ノイズ除去: ひらがなを捨てることで、意味の薄い語を自動的にカットできます。
デメリット:
- ひらがなの名詞も消える: これが最大の欠点です。「うさぎ」「さくら」「こども」といった、ひらがなだけで表記される重要な単語も消えてしまいます。
- 精度の限界: 「東京都」のような地名は残りますが、「東京」と「都」の区切り判定などはできません(今回は漢字続きなら連結されます)。
まとめ:手軽さとコストのバランス解
今回紹介した K_TOKENIZE は、完璧な形態素解析ではありません。しかし、「数億行のデータに対して、低コストで、それっぽいキーワード抽出や類似度計算を行いたい」 というシーンでは、非常に強力な武器になります。
- 用途: 類似記事のレコメンド、タグ生成の候補出し、検索インデックスの補助
-
使い分け:
- 精度最優先・ひらがな語も重要 → 素直にMeCab/Sudachi (WASM) を使う
- とにかく安く・速く・大まかな傾向を掴みたい → 今回の手法
前回の「コサイン類似度計算」の記事と組み合わせることで、BigQuery上でのレコメンド実装がより現実的なコストで運用できるようになります。ぜひお試しください。
さいごに:もっと本格的な検索・レコメンド改善をお考えの方へ
改めまして、株式会社ケンサク代表の堀田です。
今回の手法は「コストと手軽さ」に振り切ったアプローチですが、実際のビジネス課題においては、より高度な自然言語処理や、ユーザー行動ログを活用したパーソナライズが必要になることも多々あります。
- 「自社のデータで、もっと精度の高いレコメンドを作りたい」
- 「検索結果がイマイチで、CVRが上がらない」
- 「データ基盤はあるが、活用のための技術リソースが足りない」
そうしたお悩みをお持ちでしたら、ぜひ一度ご相談ください。現状のシステムやデータをヒアリングさせていただき、最適な改善プランをご提案します。

