3
5

ベクトルを用いたメモリーアクセスのアイデア。

Last updated at Posted at 2024-09-26

ベクトルを用いたメモリーアクセスのアイデアは、特に近年のGPUや高速なメモリー技術を考慮すると、理論的には実装可能な範囲にあると思います。この考え方は、いわゆる ベクトル型メモリーアーキテクチャ として捉えることができ、データのアドレス自体をベクトル空間の座標として扱うというアイデアに近いです。

ベクトル型メモリーアクセスの概念
通常のメモリーアドレスは、リニアなアドレス空間(整数のアドレス)に基づいています。これは整数という1次元の特殊なベクトルとも考えられます。しかし、ベクトル型メモリーでは、アドレスはベクトルで表され、例えばデータの特徴量や属性に基づいたベクトルが使用されると考えることができます。アクセスする際には、与えられたベクトルに対して コサイン類似度 などの手法を用いて、最も類似するメモリーアドレス(ベクトル)を見つけ、そこからデータを取得します。

ベクトル型メモリーアーキテクチャの基本的なアイデアを、Pythonで簡単な実装に落とし込んだ例を示します。このコードでは、メモリーアドレスをベクトルとして扱い、与えられたクエリベクトルとの コサイン類似度 を計算して、最も類似度が高いメモリーアドレスからデータを取り出す、という動作をシミュレートします。

まずは、簡単なメモリーバンクとしてベクトルをリストに格納し、クエリベクトルを使ってメモリーへのアクセスを行う例を示します。


import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# メモリーバンクの初期化(ここでは10個のベクトルを格納)
# ベクトルの次元は4としますが、任意の次元数にできます
memory_bank = np.random.rand(10, 4)

# メモリーバンクに格納されたデータ(ここでは単純にインデックスをデータとします)
data_bank = np.array([f"Data {i}" for i in range(10)])

# クエリベクトル(ここでは4次元のランダムベクトル)
query_vector = np.random.rand(1, 4)

# コサイン類似度を計算
similarities = cosine_similarity(query_vector, memory_bank)

# 最もコサイン類似度が高いベクトルのインデックスを取得
max_sim_index = np.argmax(similarities)

# メモリーバンクからデータを取り出し
retrieved_data = data_bank[max_sim_index]

print("Query Vector:", query_vector)
print("Most Similar Memory Address Vector:", memory_bank[max_sim_index])
print("Retrieved Data:", retrieved_data)

説明
メモリーバンクの定義:

memory_bank には、複数のベクトルが格納されており、これはメモリーのアドレスに相当します。各アドレスは4次元のベクトルで表されていますが、次元数は任意です。

データバンクの定義:

data_bank には、それぞれのメモリーアドレスに対応するデータが格納されています。この例では、シンプルに「Data 0」「Data 1」などの文字列ですが、実際のデータに置き換えることができます。

クエリベクトル:

クエリとしてランダムな4次元ベクトルを定義しています。このベクトルを元に、どのメモリーアドレス(ベクトル)が最も類似しているかを判定します。

コサイン類似度の計算:

cosine_similarity 関数を使って、クエリベクトルとメモリーバンク内の各ベクトルとの類似度を計算します。類似度が最も高いベクトルのインデックスを取得し、そのアドレスに対応するデータを取り出します。

このコードは、ユニバーサルセンテンスエンコーダーを使ってテキストをベクトル化し、クエリに対して類似する単語をトレーニングデータから見つけて置き換えるシンプルな計算コードです。

ベクトル型メモリーアーキテクチャの基本的なアイデア例。

トレーニングテキスト
ある日、静かな田舎町にある小さな学校で、教師のアキラ先生は生徒たちにプログラミングの基礎を教えようとしていました。生徒たちはコンピュータの前に座り、キーボードを叩く準備をしていましたが、多くの生徒は初めてのプログラミングに不安を感じていました。

クエリーテキスト。
生徒たちは興奮と好奇心に満ちた目で画面を見つめました。画面の右半分はプログラミングエディタになっており、コードを書いて実行ボタンを押すことで、プログラムが動作します。画面の左半分はチャット用で、何か困ったことがあればすぐに質問できるようになっています。

Resultテキスト。
生徒たちは静かに前列に座り、プログラミングを始めました。前列の多くはタイピングを始めましたが、プログラミングを始めるには、前列の生徒の多くがタイピングを始めました。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sentence Embedding with TensorFlow.js</title>
    <!-- TensorFlow.jsライブラリを読み込み -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <!-- Universal Sentence Encoderモデルを読み込み -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/universal-sentence-encoder"></script>
</head>
<body>
    <h1>Text Embedding and Replacement using Universal Sentence Encoder</h1>
    
    <h2>Training Text</h2>
    <!-- トレーニング用のテキストを入力するテキストエリア -->
    <textarea id="trainText" rows="4" cols="50" placeholder="Enter your training text here..."></textarea>
    
    <h2>Query Text</h2>
    <!-- クエリ用のテキストを入力するテキストエリア -->
    <textarea id="queryText" rows="4" cols="50" placeholder="Enter your query text here..."></textarea>
    
    <br><br>
    <!-- 処理を実行するためのボタン -->
    <button onclick="processText()">Submit</button>

    <h2>Result</h2>
    <!-- 結果を表示するためのエリア -->
    <p id="result"></p>

    <script>
        let model; // モデルをグローバル変数として宣言

        // Universal Sentence Encoderモデルをロードする関数
        async function loadModel() {
            // モデルをロード
            model = await use.load();
            console.log("Universal Sentence Encoder model loaded.");
        }

        // テキスト処理を行う関数
        async function processText() {
            // テキストエリアから入力されたトレーニングテキストとクエリテキストを取得
            const trainText = document.getElementById("trainText").value;
            const queryText = document.getElementById("queryText").value;

            // トレーニングテキストまたはクエリテキストが空の場合はエラーメッセージを表示
            if (!trainText || !queryText) {
                alert("Please enter both training and query text.");
                return;
            }

            // テキストをスペースで区切ってトークン(単語やフレーズ)に分割
            const trainTokens = trainText.split(/\s+/);
            const queryTokens = queryText.split(/\s+/);

            // トレーニングテキストとクエリテキストをエンコード(埋め込みベクトルを生成)
            const trainEmbeddings = await model.embed(trainTokens);
            const queryEmbeddings = await model.embed(queryTokens);

            // クエリトークンごとにトレーニングトークンとの類似度を計算し、結果を取得
            const resultTokens = await findSimilarTokens(trainTokens, trainEmbeddings, queryTokens, queryEmbeddings);

            // 結果テキストを表示
            document.getElementById("result").innerText = resultTokens.join(" ");
        }

        // クエリトークンごとに、最も類似したトレーニングトークンを見つける関数
        async function findSimilarTokens(trainTokens, trainEmbeddings, queryTokens, queryEmbeddings) {
            const resultTokens = []; // 置き換え結果を格納するための配列

            // クエリトークンごとにループ処理を実行
            for (let i = 0; i < queryTokens.length; i++) {
                let bestMatchIndex = -1; // 最も類似しているトークンのインデックス
                let bestSimilarity = -1; // 最も高い類似度

                // クエリトークンの埋め込みベクトルを各トレーニングトークンのベクトルと比較
                for (let j = 0; j < trainTokens.length; j++) {
                    const similarity = await cosineSimilarity(
                        queryEmbeddings.gather(i), trainEmbeddings.gather(j)
                    );

                    // 最も類似度が高いトレーニングトークンを記録
                    if (similarity > bestSimilarity) {
                        bestSimilarity = similarity;
                        bestMatchIndex = j;
                    }
                }

                // 類似度が最も高いトレーニングトークンでクエリトークンを置き換える
                if (bestMatchIndex !== -1) {
                    resultTokens.push(trainTokens[bestMatchIndex]);
                } else {
                    // 置き換えができない場合は元のクエリトークンを使用
                    resultTokens.push(queryTokens[i]);
                }
            }

            return resultTokens; // 置き換え後のトークンを返す
        }

        // 2つのベクトル間のコサイン類似度を計算するための関数
        async function cosineSimilarity(vecA, vecB) {
            // ドット積を計算
            const dotProduct = tf.dot(vecA, vecB).dataSync();
            // ベクトルのノルム(長さ)を計算
            const normA = tf.norm(vecA).dataSync();
            const normB = tf.norm(vecB).dataSync();

            // コサイン類似度を計算し返す
            return dotProduct / (normA * normB);
        }

        // ページ読み込み時にUniversal Sentence Encoderモデルをロードする
        window.onload = () => {
            loadModel();
        };
    </script>
</body>
</html>

説明
modelのロード:

ページが読み込まれた時点でuse.load()を使用してUniversal Sentence Encoderモデルをロードします。ロードが完了すると、コンソールにメッセージを出力します。
processText関数:

トレーニングテキストとクエリテキストの両方が入力されているかを確認します。もし片方が未入力の場合、エラーメッセージが表示されます。
入力されたテキストをスペースで分割してトークン化し、model.embed()を使ってそれぞれのテキストに対する埋め込みベクトルを生成します。
findSimilarTokens関数:

クエリトークンごとに最も類似しているトレーニングトークンを探します。類似度の計算にはコサイン類似度を使用します。
最も類似したトークンでクエリトークンを置き換え、最終的な結果を配列として返します。
コサイン類似度の計算:

tf.dot()を使って2つのベクトルのドット積を計算し、tf.norm()でベクトルの長さ(ノルム)を求めます。ドット積とノルムの積からコサイン類似度を算出します。
このコードは、ユニバーサルセンテンスエンコーダーを使ってテキストをベクトル化し、クエリに対して類似する単語をトレーニングデータから見つけるシンプルなコードです。

シンプルな株価検索システムのPythonコード(ベクトル検索をベース)。このコードでは、過去の株価データをベクトル化し、ユーザーが指定した特定の株価の履歴に最も近い過去の株価データを検索します。

具体的には、株価の履歴を各日ごとの終値をベクトル化し、類似性(ここではコサイン類似度)を計算して、最も類似した株価履歴を見つけます。

Pythonコード: シンプルな株価検索システム


import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# ダミーの株価データ(過去の終値)
# 各リストが1つの銘柄の株価の履歴(終値)
stock_data = {
    'AAPL': [150, 152, 149, 155, 160, 158, 162],
    'GOOG': [2700, 2720, 2690, 2750, 2800, 2780, 2820],
    'AMZN': [3400, 3450, 3380, 3500, 3550, 3520, 3570],
    'TSLA': [700, 720, 710, 740, 750, 745, 755]
}

# 類似度を計算して最も近い株価履歴を見つける関数
def find_similar_stock(target_stock, stock_data):
    target_vector = np.array(stock_data[target_stock]).reshape(1, -1)
    
    # 類似度計算のための結果リスト
    similarities = []
    
    # 各銘柄の株価履歴をベクトル化してコサイン類似度を計算
    for stock, price_history in stock_data.items():
        if stock == target_stock:
            continue  # 自身との比較を避ける
        stock_vector = np.array(price_history).reshape(1, -1)
        similarity = cosine_similarity(target_vector, stock_vector)[0][0]
        similarities.append((stock, similarity))
    
    # 類似度が最も高い銘柄を返す
    most_similar_stock = max(similarities, key=lambda x: x[1])
    return most_similar_stock

# ユーザーが検索したい銘柄を指定
target_stock = 'AAPL'

# 類似する株価を検索
similar_stock, similarity_score = find_similar_stock(target_stock, stock_data)

# 結果を表示
print(f"株価が {target_stock} に最も似ているのは {similar_stock} です。")
print(f"類似度スコア: {similarity_score:.2f}")

説明
データ構造: stock_data という辞書で、各銘柄の過去の終値を保持しています。この例では、AAPL, GOOG, AMZN, TSLA の4つの銘柄があります。

コサイン類似度計算: 株価データをベクトル化し、cosine_similarity 関数でベクトル間の類似性を計算します。コサイン類似度は、2つのベクトル間の角度を元に、値が1に近いほど類似していることを示します。

検索アルゴリズム: find_similar_stock 関数で、ユーザーが選んだ銘柄と他の全ての銘柄との間の類似度を計算し、最も類似している株価を見つけます。

結果表示: 最終的に、最も似ている株価を持つ銘柄とその類似度スコアを表示します。

実行例
例えば、AAPL の株価に最も似ている他の銘柄を探すと、次のような出力になります。

株価が AAPL に最も似ているのは TSLA です。
類似度スコア: 0.99

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5