meetup app osaka@9 で SQL Server ベクトル検索の話しをしてきました #meetupapp

meetup app@osaka9 で話して来ました。
meetup app osaka@9 - connpass

資料はこちら。
speakerdeck.com

ベクトル検索 の話しなので、ベクタライズで AI 使って~ みたいなのは無し!
デモで使った クエリ はこちら。

自前でベクタライズのは、百人一首 の歌を自前で3次元ベクトル化してます。

  • 1次元:季節 (春:1, 夏:2, 秋:-1, 冬:-2)
  • 2次元:自然 (山とか海とか歌にある毎に 1ずつ加算)
  • 3次元:動物 (動物が出たら 1ずつ加算)

検索ワードをベクトル化は、決まった文言が入ってたら、
この次元で~ としています。
AI 使わなくてもベクトル検索出来るよーってことで。

vector_distance の例

declare @v1 vector(2) = '[1, 0]';
declare @v2 vector(2) = '[0, 1]';

select 
  vector_distance('cosine', @v1, @v2) as [コサイン距離]
  , vector_distance('euclidean', @v1, @v2) as [ユークリッド距離]
  , vector_distance('dot', @v1, @v2) as [負のドット積]
;
declare @v1 vector(2) = '[0, 0]';
declare @v2 vector(2) = '[1, 1]';
declare @v3 vector(2) = '[2, 2]';
declare @v4 vector(2) = '[3, 3]';

-- 同じベクトルのコサイン距離なので、全て同一の 0 になるはず
select 
    cast(vector_distance('cosine', @v1, @v1) as decimal(38, 30)) as [コサイン1] -- 0, 0 はなんかダメ
    , cast(vector_distance('cosine', @v2, @v2) as decimal(38, 30)) as [コサイン2] -- 誤差出る
    , cast(vector_distance('cosine', @v3, @v3) as decimal(38, 30)) as [コサイン3] -- 誤差出る
    , cast(vector_distance('cosine', @v4, @v4) as decimal(38, 30)) as [コサイン4]

どの距離使うのがいい?

declare @v1 vector(2) = '[-0.5, -0.5]'
declare @v2 vector(2) = '[3, 3]'
declare @v3 vector(2) = '[0.5, 0.5]'

select 
  vector_distance('cosine', @v1, @v3) as [-0.5_コサイン距離]
  , vector_distance('euclidean', @v1, @v3) as [-0.5_ユークリッド距離]
  , vector_distance('dot', @v1, @v3) as [-0.5_負のドット積]
  , vector_distance('cosine', @v2, @v3) as [3_コサイン距離]
  , vector_distance('euclidean', @v2, @v3) as [3_ユークリッド距離]
  , vector_distance('dot', @v2, @v3) as [3_負のドット積]

自前ベクトル化のサンプル DDL

create table [百人一首] (
  [No] int not null
  , [歌] nvarchar(200) not null
  , [ベクトル] vector(3) not null
);

insert into [百人一首] ([No], [歌], [ベクトル]) values 
(2, N'春過ぎて 夏来にけらし 白妙の 衣ほすてふ 天の香具山持統天皇(645年~702年)', '[2, 1, 0]')
, (3, N'あしびきの 山鳥の尾の しだり尾の ながながし夜を ひとりかも寝む柿本人麻呂(生没年不詳)', '[-1, 0, 0]')
, (4, N'田子の浦に うちいでてみれば 白妙の 富士の高嶺に 雪は降りつつ山部赤人(生没年不詳)', '[-2, 2, 0]')
, (5, N'奥山に 紅葉踏み分け 鳴く鹿の 声聞く時ぞ 秋はかなしき猿丸太夫(生没年不詳)', '[-1, 1, 1]')
, (6, N'かささぎの 渡せる橋に おく霜の 白きをみれば 夜ぞふけにける中納言家持(718年頃~785年)', '[-2, 0, 0]')
;

create table [季節] (
  [文言] nvarchar(10) not null
  , [ベクトル値] int
);
insert into [季節] ([文言], [ベクトル値]) values 
(N'', 1), (N'', 2), (N'', -1), (N'', 2), (N'', 2), (N'', -2), (N'', 1), (N'', -2), (N'', -1);

create table [自然] (
  [文言] nvarchar(10) not null
  , [ベクトル値] int
);
insert into [自然] ([文言], [ベクトル値]) values 
(N'', 1), (N'', 1), (N'', 1), (N'', 1), (N'自然', 1);

create table [動物] (
  [文言] nvarchar(10) not null
  , [ベクトル値] int
);
insert into [動物] ([文言], [ベクトル値]) values 
(N'動物', 1)
;

create function [ベクトル化] (
  @v nvarchar(100)
) returns vector(3)
as 
begin
  declare @p1 int = isnull((select top(1) sum([ベクトル値]) from [季節] where @v like concat('%', [文言], '%')), 0);
  declare @p2 int = isnull((select top(1) sum([ベクトル値]) from [自然] where @v like concat('%', [文言], '%')), 0);
  declare @p3 int = isnull((select top(1) sum([ベクトル値]) from [動物] where @v like concat('%', [文言], '%')), 0);

  declare @ret vector(3) = concat('[', @p1, ',', @p2, ',', @p3,']')
  
  return @ret;
end

自前のベクトル化のサンプル

select * from [百人一首]
;

declare @searchWord nvarchar(100) = N'涼しい季節の歌'
;

select
  [No]
  , [歌]
  , vector_distance('cosine', [ベクトル], [dbo].[ベクトル化](@searchWord)) as [コサイン距離]
from [百人一首]
where 
  vector_distance('cosine', [ベクトル], [dbo].[ベクトル化](@searchWord)) < 0.1
order by 
  vector_distance('cosine', [ベクトル], [dbo].[ベクトル化](@searchWord))
;
select
  [No]
  , [歌]
  , vector_distance('euclidean', [ベクトル], [dbo].[ベクトル化](@searchWord)) as [ユークリッド距離]
from [百人一首]
where 
  vector_distance('euclidean', [ベクトル], [dbo].[ベクトル化](@searchWord)) < 1.5
order by
  vector_distance('euclidean', [ベクトル], [dbo].[ベクトル化](@searchWord))
;
select
  [No]
  , [歌]
  , vector_distance('dot', [ベクトル], [dbo].[ベクトル化](@searchWord)) as [負のドット積]
from [百人一首]
order by 
  vector_distance('dot', [ベクトル], [dbo].[ベクトル化](@searchWord))
;