Google スプレッドシートで、自然言語で質問できる関数を作ってみよう(Google Cloud Vertex AI 前編)

2023年9月29日掲載

img-techblog-kv-googlecloud.png

システムエンジニアの成瀬です。

日々刻々と進化を続けているAIやクラウドサービスを検証したり、AIを活用したアプリを作ったりしています。

目次

  • この記事では、Google スプレッドシートでカスタム関数を活用する方法を紹介しています。
  • 活用する例として、Google Cloud Vertex AI のAPIとの連携方法を説明しています。

考えてみよう

似ているか、似ていないか

とつぜんですが、赤、オレンジ、青の3つの色について、オレンジは、赤と青のどちらに似ていると思いますか?

fig.1

多くの人は、オレンジは青よりも赤に似ていると思うのではないでしょうか?

でも、その理由を答えることができますか?

よくある理由としては、オレンジは赤っぽいだとか、赤とオレンジは暖色系だからといったところでしょうか?

それでは、青よりも赤の方が似ていると判断される根拠を、もう少し探ってみたいと思います。

光のスペクトル

理科の授業などで誰でも一度は目にしている、光のスペクトルを見てみましょう。

光のスペクトルは、人間の眼が認識可能な光の波長の長さの順にならんでいて、この中にも赤、オレンジ、青が含まれています。

それぞれの色のだいたいの位置をマークしてみます。

fig.1

これを見ても、オレンジは赤に近く、青は遠くにあることから、オレンジは青より赤に似ていると言えそうです。

つまり、色どうしの「似ている」か、「似ていない」かは、光の波長が示す位置が、「近い」か「遠い」かによって説明できると言えるのではないでしょうか?

RGBで考えてみる

コンピュータの世界ではおなじみのRGBカラーモデルは、光の三原色(赤緑青)の明るさ(輝度)の組み合わせで表現します。

昔のPCなどはRGBの成分ごとに0か1しか選択できなかったため、1ピクセルが表現できる色は8色しかありませんでした。(つまり、オレンジという色は存在しなかったのです...)

RGB

0

0

0

0

0

1

0

1

0

シアン

0

1

1

1

0

0

マゼンタ

1

0

1

黄色

1

1

0

1

1

1

現代のPCでは、RGBごとに0から255の256階調で、約1600万色の表現が一般的となっています。

例えば、オレンジを256階調のRGBで表すと、 R=255, G=127, B=0となります。

私のオレンジは違うという方がいるかもしれませんが、オレンジはどこまでがオレンジかというパラドックスに陥ってしまうので、この場はお許しください。

光の三原色の赤・緑・青とオレンジの各RGBの値を表にまとめると以下のようになります。

RGB

255

0

0

0

255

0

0

0

255

オレンジ

255

127

0

さて、上の表はR、G、Bの各成分を軸とした3次元データと考えることができます。

それぞれの色を3次元空間上にプロットしてみましょう。

fig.1

赤、緑、青はそれぞれお互いに対極に位置していて、赤とオレンジは近い位置にいることがわかります。

距離を測ってみる

2点の位置が近いか遠いかを正確に判断するためには、実際に距離を測って比べてみればよいですね。

オレンジと赤の距離は、以下の式で求めることができます。

 (オレンジのR座標 - 赤のR座標)^2 + (オレンジのG座標 - 赤のG座標)^2 + (オレンジのB座標 - 赤のB座標)^2 

スプレッドシートでの計算式は以下の通りで、結果は127となりました。

=SQRT((255 - 255)^2 + (127 - 0)^2 + (0 - 0)^2)

同様にそれぞれの色について、お互いの距離を計算した結果は以下の通りとなりました。

 オレンジ

0

360.62

360.62

127

360.62

0

360.62

285.32

360.62

360.62

0

382

オレンジ

127

285.32

382

0

オレンジと青の距離が382に対して、オレンジと赤の距離は127となりました。

これで、オレンジは青より赤に似ている(近い)ことが、より明確になったのではないでしょうか?

角度を測ってみる

円周上に等間隔に色を配置した、カラーホイールという図があります。

fig.1

この図を使って、色どうしの中心からの角度を考えてみましょう。

上のカラーホイールは12色あるので、隣どうしの色の角度は30度になります。

それぞれの色について、お互いの角度を計算した結果は以下の通りです。

 オレンジ

120°

120°

30°

120°

120°

90°

120°

120°

150°

オレンジ

30°

90°

150°

オレンジと赤が30°に対して、オレンジと青は150°でした。

角度を測る意味として、色の階調(スケール)が変わった場合には、距離も変わってしまいますが、角度は一定のままというメリットがあります。

座標から角度を測る方法

ここでいったん、オレンジのことは忘れて、赤と黄色と緑について考えてみたいと思います。

それぞれの色のRGB成分は以下の表の通りです。

RGB

255

0

0

黄色

255

255

0

0

255

0

いずれの色もB(青)の成分が0なので、RとGの2次元データとして考えることができます。

ここでRとGの各成分の値の範囲を、「0から255の整数」から、「-1.0から1.0の実数」に変換しておきます。(理由は後で説明します)

つまり、最も輝度の高い255が1.0、中間の127が0.0、最も輝度が低い0が-1.0となります。

変換した結果は以下の通りです。

RG

1.0

-1.0

黄色

1.0

1.0

-1.0

1.0

これらの色をR軸とG軸の平面上にプロットしてみます。

fig.1

さらに、原点 (0, 0)から、それぞれの色の座標に線を引いてみます。

fig.1

こうすることで、線と線に挟まれた角度を測ることができるようになります。

(単位系を変換したのは、原点を中心に対極に配置するためでした)

角度は下の図のように、反時計回りに0~180°の範囲になるように測ります。

fig.1

角度が0°ということは同一なので完全一致、角度が180°ということはは正反対なので完全不一致になります。

代表的な角度と似ている・似ていないの関係を表にまとめておきます。

角度似ている・似ていない

完全一致

45°

似ている

90°

どちらでもない

135°

似ていない

180°

完全不一致

見た目の上では、赤と黄色は90°でどちらでもない、赤と緑は180°なので完全不一致といったところでしょうか。

線と線の間の角度を測る

赤と黄色の線の間の角度を測るためには、以下の式1~4を計算します。

fig.1

では、実際に計算してみましょう。

赤と黄色のRG成分を再掲しておきます。

RG

1.0

-1.0

黄色

1.0

1.0

式1のスプレッドシートでの計算式は以下の通りで、結果は0.0となります。

=(1.0 * 1.0) + (-1.0 * 1.0)

式2のスプレッドシートでの計算式は以下の通りで、結果は1.41…(√2)となります。

=SQRT(1.0^2 + -1.0^2)

式3のスプレッドシートでの計算式は以下の通りで、結果は1.41…(√2)となります。

=SQRT(1.0^2 + 1.0^2)

式4は分子が0.0なので、結果も0.0です。

=0.0/(1.41... * 1.41...)

さて、結果は90になることを予想していましたが、実際は0.0でした。何か間違っていたのでしょうか?

実は、式4で算出された値は求めたかった角度ではなく、その角度に対するコサイン(cos θ)に等しい値です。

したがって、角度自体を求めるには 計算結果の0.0を逆コサインする必要があります。

スプレッドシート関数だと計算式は以下の通りで、結果は1.57…となります。

=ACOS(0.0)

さらに、逆コサインの単位はラジアン(円周=2πとする単位)であるため、スプレッドシート関数の以下の計算式で角度に変換します。

=DEGREES(1.57...)

計算結果は90になるので、予想していた角度になりました。

さらに、cosθが0.0のときの角度は90°であるということもわかりました。

念のため、角度が90°のときのコサインも計算しておきましょう。

まず、90°をラジアンに変換します。

=RADIANS(90)

結果は1.57…(2/4π)となりました。

次に、ラジアンに変換した値のコサインを計算します。

=COS(1.57...)

結果は0.0となり、角度が90°のときのコサインは0.0になるということが確認できました。

代表的なcos θと角度(ラジアン)との関係を以下にまとめておきます。

cos θ角度ラジアン
1.00

0

0.70...

45°

1/4π (0.78...)

0.00

90°

2/4π(1.57...)

-0.70...

135°

3/4π(2.35...)

-1.00

180°

4/4π(3.14...)

cos θ = ?

さて、ここであらためて「cos θ」に着目してみましょう。

cos θが、1.0の場合は角度が0°なので完全一致、-1.0の場合は角度が180°なので完全不一致といえます。

つまり、角度を計算するまでもなく cos θが示す値を「似ている」か、「似ていない」かの尺度として使えるわけです。

これをコサイン類似度と呼んでいます。

コサイン類似度の意味と角度の関係を表にまとめておきます。

コサイン類似度
(cos θ)
意味角度ラジアン
1.00 (√4/2)

完全一致

0

0.70... (√2/2)

似ている

45°

1/4π (0.78...)

0.00 (√0/2)

どちらでもない

90°

2/4π(1.57...)

-0.70... (-√2/2)

似ていない

135°

3/4π(2.35...)

-1.00 (-√4/2)

完全不一致

180°

4/4π(3.14...)

立体(3次元)上のコサイン類似度

では本題に戻って、3次元上での赤、オレンジ、青についてコサイン類似度を計算してみましょう。

計算の考え方は上の平面の場合と同じで、計算対象の次元が一つ増えるだけです。

fig.1

オレンジと赤のコサイン類似度の計算式は以下の通りです。

fig.1

では、実際に計算してみましょう。

-1.0~1.0の実数に変換した各色のRGB成分は以下の通りです。

RGB

1.0

-1.0

-1.0

-1.0

1.0

-1.0

-1.0

-1.0

1.0

オレンジ

1.0

0.0

-1.0

式1の計算式は以下の通りで、結果は2.0となります。

=( 1.0 * 1.0 ) + ( -1.0 * 0.0 ) + ( -1.0 * -1.0) 

式2の計算式は以下の通りで、結果は1.732…(√3)となります。

=SQRT( 1.0^2 + -1.0^2 + -1.0^2 )

式3の計算式は以下の通りで、結果は1.414…( √2)となります。

=SQRT( 1.0^2 + 0.0^2 + -1.0^2 )

式4の計算式は以下の通りで、結果は0.81…( 2/√6)となります。

=2.0 / ( 1.732... * 1.414...) 

同様に、各色どうしのコサイン類似度の算出結果を以下の表にまとめておきます。

 オレンジ

1.00

-0.33

-0.33

0.81

-0.33

1.00

-0.33

0.44

-0.33

-0.33

1.00

-0.81

オレンジ

0.81

0.44

-0.81

1.00

言葉で表現すれば、オレンジと赤は「かなり似ている」けど、オレンジと青は「かなり似ていない」と言ったところでしょうか。

ベクトル埋め込み

今回利用する、Vertex AI の Text Embeddings APIは、テキストを渡すと埋め込みによって768次元のベクトルが返ってきます。

ここでのベクトルとは実数の並びを意味していて、埋め込みとはデータをモデルの特徴空間上の点にマッピングすることです。

RGBカラーモデルの例でいえば、オレンジと入力すれば、[1.0, 0.0, -1.0]の3次元データが返ってくることをイメージすると、わかりやすいかと思います。

(ただし、各次元の成分はRGBのようにあらかじめ決まっているわけではありません)

768次元であっても、平面や立体と同様にベクトル間の距離や類似度を計算することができます。

作ってみよう

Vertex AI APIを利用するための準備

Vertex AI APIを利用するためには、APIを有効にする必要があります。

また、Vertex AI のAPIをプログラムから呼び出すために、サービスアカウントを作成する必要があります。

Vertex AI のAPIを有効化

  1. Google Cloudのコンソールにログインして、プロジェクトを作成または選択します。
fig.1
  1. 左上のナビゲーションメニューをクリックして、「API と サービス」 -> 「有効な API とサービス」を選択します。
fig.1
  1. 「+ API とサービスの有効化」をクリックします。
fig.1
  1. 検索ボックスに「vertex」と入力します。
fig.1
  1. 検索結果の「Vertex AI API」をクリックします。
fig.1
  1. 「有効にする」をクリックします。
fig.1

サービスアカウントの作成

サービスアカウントで、Vertex AI APIのエンドポイント予測を実行するために必要な権限は以下になります。

「aiplatform.endpoints.predict」

上の権限に該当するロールは以下のいずれかになります。

  • Vertex AI Service Agent 
  • Vertex AI User

今回は「Vertex AI Service Agent」をロールとして割り当てることにします。

  1. 左上のナビゲーションメニューをクリックして、「IAM と 管理」 -> 「サービス アカウント」を選択します。
fig.1
  1. 「+ サービス アカウントを作成」をクリックします。
fig.1
  1. 以下の情報を入力します。
    • サービスアカウント名
    • サービスアカウントの説明
  1. 「作成して続行」をクリックします。
fig.1
  1. 「ロール」をクリックして、「Vertex AI サービスエージェント」を選択します。
  2. 「完了」をクリックします。
fig.1
  1. 作成したサービスアカウントの右側の「︙」をクリックして、「鍵を管理」を選択します。
fig.1
  1. 「鍵を追加」をクリックして、「新しい鍵を作成」を選択します。
fig.1
  1. 秘密鍵の作成ダイアログで「作成」をクリックします。
fig.1
  1. サービスアカウントのキーファイル(JSON)が、ローカルのダウンロードフォルダに保存されます。
    ※ キーファイルは機密情報なので、大切に保管しておいてください。
  2. 「閉じる」をクリックします。
fig.1

テキスト埋め込みを取得するカスタム関数

テキスト埋め込みを取得するカスタム関数の使用方法を以下のように決めておきます。

概要

Vertex AI APIを実行してテキストに対する埋め込みベクトルを取得する関数です。

関数の構文

TEXT_EMBEDDINGS( テキスト)

テキスト:埋め込みベクトルを取得する対象のテキストです。

戻り値

文字列化された768次元のベクトルです。

(※ 扱いやすくするために、文字列化しておきます)

使用例

TEXT_EMBEDDINGS( "トイレに行きたいのですが" )

Vertex AI Text Embeddings APIの使用方法

基盤モデル

現在、利用可能なベクトル埋め込みの基盤モデルは「textembedding-gecko」のみで、日本語には未対応のようです。

モデル名textembedding-gecko
最大入力トークン数3072
出力ベクトル次元数768
対応言語English (en)
Spanish (es)
Korean (ko)
Hindi (hi)
Chinese (zh)

リクエストURL

POST https://us-central1-aiplatform.googleapis.com/v1/projects/{プロジェクトID}/locations/us-central1/publishers/google/models/textembedding-gecko:predict

リクエストヘッダ

名称説明

Content-Type

(必須)

application/jsonリクエストデータの種類(JSON)

Authorization

(必須)

"Bearer {アクセストークン}"サービス認証に必要

※ アクセストークンは別途APIで取得する必要があります。取得方法については、「アクセストークンの取得方法」で説明します。

リクエストデータ (JSON)

フィールド名データ型説明
instancesオブジェクト配列

最大5件まで

 contentテキスト埋め込みベクトルに変換するためのテキスト
3072トークン以内
parametersオブジェクト

 

 opt_outブーリアン学習データへの利用を許可するかどうか
  • true: 許可しない
  • false: 許可する

リクエストデータのサンプル

{
  instances: [
    {
      content: "Where is the restroom?"
    }
  ],
  parameters: {
    opt_out: true
  }
}

レスポンスデータ (JSON)

フィールド名データ型説明
predictionsオブジェクト配列結果情報
 embeddingsテキスト埋め込み情報
  values実数の配列

埋め込みベクトル

(実768次数ベクトル)

  statisticsオブジェクト統計情報
   token_count整数トークン数
   truncatedブーリアン

切り詰められたかどうか

  • true: 切り詰められた
  • false: 切り詰められていない

レスポンスデータのサンプル

predictions: [
  {
    embeddings: {
      values: [ 0.04106621444225311, 0.015827510505914688, ... ],
	statistics: {
        truncated: false, 
        token_count: 5
      }
    }
  }    
]

上のサンプルの場合、カスタム関数の戻り値として埋め込みベクトルを返すためには、レスポンスデータの predictions[0].embeddings.values を文字列に変換してセットすればよいことになります。

なお、Vertex APIのリクエストは、1分あたり30回の利用制限があります。

利用制限を超過した場合、以下のエラーメッセージが返ってくるので、すこし待ってからリトライしてみてください。

{
  code: 429, 
  message: 'Quota exceeded for aiplatform.googleapis.com/online_prediction_requests_per_base_model with base model: textembedding-gecko. Please submit a quota increase request. https://cloud.google.com/vertex-ai/docs/quotas.',
  status: 'RESOURCE_EXHAUSTED'
}

アクセストークンの取得方法

Vertex AI APIの認証に必要なアクセストークンは、Google OAuth APIを使用して取得します。

アクセストークンを取得する処理は、カスタム関数とは別に共通関数として定義します。

リクエストURL

POST https://www.googleapis.com/oauth2/v4/token/

リクエストヘッダ

名称説明

Content-Type

(必須)

application/jsonリクエストデータの種類(JSON)

リクエストデータ (JSON)

フィールド名データ型説明
payloadテキスト

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion={認証トークン}

※ 認証トークンはJWT(Json Web Token)形式です。詳しい作りかたはここでは割愛しますが、使用するクレームだけ表にまとめておきます。

属性
HEADER 
 algRS256
 typJWT
PAYLOAD
 issサービスアカウントキーのclient_email
 scopehttps://www.googleapis.com/auth/cloud-platform
 audhttps://www.googleapis.com/oauth2/v4/token
 iat認証トークンの作成時刻(エポック秒)
 exp認証トークンの有効期限(エポック秒)

レスポンスデータ (JSON)

フィールド名データ型説明
access_tokenテキストアクセストークン
expires_in整数有効期限(秒)
token_typeテキスト

「Bearer」固定

レスポンスデータのサンプル

{
  access_token: 'ya29...Uxt8',
  expires_in: 3599,
  token_type: 'Bearer'
}

ベクトル間の類似度を計算するカスタム関数

ベクトル間の類似度を計算するカスタム関数の使用方法を以下のように決めておきます。

概要

ベクトルどうしの類似度を計算します。

関数の構文

VECTOR_SIMILARITY(ベクトル1, ベクトル2)

ベクトル1:比較する一方のベクトルです。

ベクトル2:比較するもう一方のベクトルです。

※各ベクトルは文字列化されていて、次元数が一致している必要があります。

戻り値

1.0から-1.0の範囲の実数です。

1.0が最も似ていて、-1.0が最も似ていません。

使用例

VECTOR_SIMILARITY("[-0.027284255251288414,...]", "[-0.049530670046806335,...]")

上でも出てきたように、ベクトルAとBのコサイン類似度(cos θ)の計算式を以下にまとめておきます。

fig.1

カスタム関数の登録

Google スプレッドシートのカスタム関数は、スプレッドシートとは別の画面のスクリプトエディタから登録します。

  1. スプレッドシートのメニューから、「拡張機能」 -> 「Apps Script」を選択して、スクリプトエディタを開きます。
fig.26
  1. ファイル「コード.gs」内の1〜3行目を選択して、デリートキーかバックスペースキーを押してクリアします。
fig.27
  1. 以下のコードをすべて選択してコピーします。
// サービスアカウントキー
const GCP_CREDS =
// この行を選択して、サービスアカウントのキーファイル(JSON)の内容をペーストしてください



/**
 * サービスアカウントでAPIを利用するためのアクセストークンを取得します。
 * カスタム関数の内部で使用するための共通関数です。
 *
 * @param {string} client_email  資格情報のclient_email
 * @param {string} private_key  資格情報のprivate_key
 * @param {number} expires  トークンの有効期間(秒) デフォルト値:1800 
 * 
 * @return {object} 実行結果
 * 
 * copyright (c) 2023 SoftBank Corp.
 */
function get_access_token_(client_email, private_key, expires = 1800) {

  const header = { "alg": "RS256", "typ": "JWT" };

  const now = new Date();
  const iat = Math.floor(now / 1000)
  const exp = iat + expires

  const payload = {
    "iss": client_email,
    "scope": "https://www.googleapis.com/auth/cloud-platform",
    "aud": "https://www.googleapis.com/oauth2/v4/token",
    "iat": iat,
    "exp": exp,
  }

  const header_encoded = Utilities.base64EncodeWebSafe(JSON.stringify(header))
  const payload_encoded = Utilities.base64EncodeWebSafe(JSON.stringify(payload)).replace(/=+$/, '')
  const to_sign = `${header_encoded}.${payload_encoded}`
  const signature = Utilities.computeRsaSha256Signature(to_sign, private_key);
  const signature_encoded = Utilities.base64EncodeWebSafe(signature).replace(/=+$/, '');
  const jwt = `${to_sign}.${signature_encoded}`;

  const params = {
    'method': 'post',
    "payload": `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`,
    'muteHttpExceptions': true
  };

  // APIリクエスト
  let res;
  try {
    res = UrlFetchApp.fetch("https://www.googleapis.com/oauth2/v4/token/", params)
  }
  catch (e) {
    console.error(e)
    throw Error("内部エラーが発生しました")
  }

  const res_json = JSON.parse(res.getContentText())
  return res_json
}



/**
 * テキスト埋め込みAPIを使用して、テキストに対する埋め込みベクトルを取得します。
 * 結果は文字列化されたベクトルです。
 *
 * @param {"トイレはどこですか"} TEXT テキスト {文字列} 
 *     埋め込み対象のテキストです。
 * @return {string}
 * 
 * @customfunction
 * copyright (c) 2023 SoftBank Corp.
 */
function TEXT_EMBEDDINGS(TEXT) {

  const url = `https://us-central1-aiplatform.googleapis.com/v1/projects/${GCP_CREDS.project_id}/locations/us-central1/publishers/google/models/textembedding-gecko:predict`

  let access_info;
  try {
    access_info = get_access_token_(GCP_CREDS.client_email, GCP_CREDS.private_key)
  }
  catch (e) {
    console.error(e)
    throw "内部エラーが発生しました"
  }
  const access_token = access_info.access_token

  const headers = {
    "Authorization": "Bearer " + access_token
  }

  const payload = {
    "instances": [
      {
        "content": TEXT,
      }
    ],
    "parameters": {
      "opt_out": true,
    }
  }

  const params = {
    method: "POST",
    headers: headers,
    contentType: "application/json",
    payload: JSON.stringify(payload),
    muteHttpExceptions: true,
  }

  const res = UrlFetchApp.fetch(url, params)
  const res_json = JSON.parse(res.getContentText())

  if ("error" in res_json) {
    console.log(res_json.error)
    throw "内部エラーが発生しました"
  }

  const vector = res_json.predictions[0].embeddings.values
  return JSON.stringify(vector)
}



/**
 * ベクトル間の類似度を算出します。各ベクトルは次元数が一致していて、文字列化されている必要があります。
 * 結果は1.0(もっとも似ている)から-1.0(もっとも似ていない)の範囲の実数です。
 *
 * @param {"[0.0,0.1]"} VECTOR1 ベクトル {文字列} 
 *     比較する一方のベクトルです。
 * @param {"[-1.0,1.1]"} VECTOR2 ベクトル {文字列} 
 *     比較するもう一方のベクトルです。
 * @return {string}
 * 
 * @customfunction
 * copyright (c) 2023 SoftBank Corp.
 */
function VECTOR_SIMILARITY(VECTOR1, VECTOR2) {

  let vec1
  try {
    vec1 = JSON.parse(VECTOR1)
  }
  catch (e) {
    throw "引数:VECTOR1が不正な値です"
  }

  let vec2
  try {
    vec2 = JSON.parse(VECTOR2)
  }
  catch (e) {
    throw "引数:VECTOR2が不正な値です"
  }

  if (vec1.length != vec2.length) {
    throw "次元数が一致していません"
  }

  // コサイン類似度を計算
  const vec12_dot = vec1.reduce((acc, curr, index) => acc + curr * vec2[index], 0);
  const vec11_dot = vec1.reduce((acc, curr, index) => acc + curr * vec1[index], 0);
  const vec22_dot = vec2.reduce((acc, curr, index) => acc + curr * vec2[index], 0);
  const vec1_l2norm = Math.sqrt(vec11_dot)
  const vec2_l2norm = Math.sqrt(vec22_dot)
  const cos_theta = vec12_dot / (vec1_l2norm * vec2_l2norm)

  return cos_theta
}
  1. コピーしたコードを、ファイル「コード.gs」内にペーストします。
fig.1
  1. 「サービスアカウントの作成」でダウンロードしたキーファイル(JSON)をテキストエディターで開きます。
  2. 全て選択して、コピーします。
fig.1
  1. スクリプトエディタに切り替えます。
  2. ファイル「コード.gs」の3行目(// この行を選択して、サービスアカウントのキーファイル(JSON)の内容をペーストしてください)を選択して、ペーストします。
    (先頭と末尾のカッコが抜けないように注意してください)
fig.1
  1. 保存ボタンをクリックします。
    (スクリプトエディターは閉じてしまっても大丈夫です)
fig.1

カスタム関数を試してみよう

それでは、実際にカスタム関数を使って、以下の4つの料理の類似度を比較してみましょう

fig.1

なお、この記事の執筆時点では、まだ日本語に対応できていないようなので、いったん英語に翻訳したテキストをカスタム関数(API)に渡します。

炒飯 -> Fried Rice

チャーシュー麺 -> Braised Pork Ramen

オムライス -> Omelette Rice

ナポリタン -> Ketchup-seasoned Pasta

  1. まず、シートに料理の名前と英語の名前をそれぞれ入力しておきます。
fig.1
  1. 次に、料理の英語名から埋め込みベクトルを取得するために、セルC3にカスタム関数の式 「=TEXT_EMBEDDINGS(B3)」を入力します。
fig.1
  1. 同様に、セルC4~C6にも以下の表の通りに入力します。
セル入力値
C3

=TEXT_EMBEDDINGS(B3)

C4

=TEXT_EMBEDDINGS(B4)

C5

=TEXT_EMBEDDINGS(B5)

C6

=TEXT_EMBEDDINGS(B6)

  1. 次に、料理と料理の類似度を算出するために、セルD3にカスタム関数の式「=VECTOR_SIMILARITY($C3, $C3)」を入力します。
fig.1
  1. 同様に、残りのセルにも以下の表の通りに入力します。
セルD列E列F列G列
行3

=VECTOR_SIMILARITY($C3,$C3)

=VECTOR_SIMILARITY($C3,$C4)

=VECTOR_SIMILARITY($C3,$C5)

=VECTOR_SIMILARITY($C3,$C6)

行4

=VECTOR_SIMILARITY($C4,$C3)

=VECTOR_SIMILARITY($C4,$C4)

=VECTOR_SIMILARITY($C4,$C5)

=VECTOR_SIMILARITY($C4,$C6)

行5

=VECTOR_SIMILARITY($C5,$C3)

=VECTOR_SIMILARITY($C5,$C4)

=VECTOR_SIMILARITY($C5,$C5)

=VECTOR_SIMILARITY($C5,$C6)

行6

=VECTOR_SIMILARITY($C6,$C3)

=VECTOR_SIMILARITY($C6,$C4)

=VECTOR_SIMILARITY($C6,$C5)

=VECTOR_SIMILARITY($C6,$C6)

  1. これで、各料理間の類似度が求められました。
fig.1

算出された各料理間の類似度をまとめた結果は以下の通りです。

 炒飯チャーシュー麺オムライスナポリタン最高最低
炒飯

1.00

0.71

0.80

0.67

オムライス

ナポリタン

チャーシュー麺

0.71

1.00

0.69

0.73

ナポリタン

オムライス

オムライス

0.80

0.69

1.00

0.67

炒飯

ナポリタン

ナポリタン

0.67

0.73

0.67

1.00

チャーシュー麵

炒飯

オムライス

どうですか? なかなか興味深い結果になりましたね。

炒飯に一番よく似ているのがオムライスなのは、両方ともご飯ものだからでしょうか。

そして、ナポリタンよりチャーシュー麵の方が炒飯に近いのは、同じ中華料理だからですかね。

ナポリタンとチャーシュー麺、炒飯とオムライスの関係についても同様のことが言えそうです。

いずれの結果もあまり違和感がないように感じますがいかがでしょうか?

ここまでのまとめ & 続編に続きます

この前編では、Vertex AI Text Embeddings API を使って、テキストの類似を判別する方法について説明しました。

この方法だけでも、似たような質問に対する回答を検索することは可能ですが、次の後編では生成AIのAPIを使って、質問に関連する文章から回答を生成するカスタム関数を作ってみようと思います。

どうぞお楽しみに!

Vertex AI DIYプランの紹介

「活用のためのアイデア」でも触れた通り、Q&Aの精度を上げるためには色々な試行錯誤が必要です。また、データセットが大規模になるほど、精度を維持することも難しくなります。

そこで、生成AI導入やシステム構築に不安がある方向けに「Vertex AI DIYプラン」をご紹介します。

Vertex AI DIYプランでは、Vertex AI Search による、 社内文書などのエンタープライズ検索の構築をエンジニアがサポートしてくれます。

興味のある方は、一度相談してみてはいかがでしょうか?

詳細については、関連サービスの「Vertex AI DIYプラン」からご確認ください。

関連サービス

Vertex AI DIYプラン

Vertex AI Search を使って社内文書を検索する生成AIを構築してみませんか?

ソフトバンクのエンジニアが構築をサポートします。

Google の生成AIの導入を考えている方はもちろん、どのようなものか確認したいという方でもご活用いただけます。

Google Workspace

Google スプレッドシート、Gmail、Google カレンダー、Google Chat、Google ドライブ、Google Meet などのさまざまなサービスがあらゆる働き方に対応する業務効率化を実現します。

Google Cloud

Google サービスを支える、信頼性に富んだクラウドサービスです。お客さまのニーズにあわせて利用可能なコンピューティングサービスに始まり、データから価値を導き出す情報分析や、最先端の機械学習技術が搭載されています。

おすすめの記事

条件に該当するページがございません