Rustによる高性能HOF認知Flash Attention機構化

著者: Asher Bond

Flash Attention機構化器、HOF認知、分散表現管理のRust実装を順を追って見ていきましょう。環境を構築し、サーバを起動し、クライアント側からHTTPリクエストを送って実装を検証します。

段階を追ったテスト

ステップ1: プロジェクトを構築する

Cargo.tomlにすべての依存関係が揃っていることを確認してください。

[dependencies]
ndarray = "0.15.4"
ndarray-rand = "0.13.0"
rand = "0.8.5"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
hyper = "0.14"
tokio = { version = "1", features = ["full"] }

ステップ2: モジュールを実装する

src/flash_attention.rs
use ndarray::{Array2, Axis};
use ndarray_rand::RandomExt;
use rand::distributions::Uniform;

pub struct FlashAttention {
 pub query: Array2<f32>,
 pub key: Array2<f32>,
 pub value: Array2<f32>,
}

impl FlashAttention {
 pub fn new(dim: usize) -> Self {
 let distribution = Uniform::new(-0.1, 0.1);
 FlashAttention {
 query: Array2::random((dim, dim), distribution),
 key: Array2::random((dim, dim), distribution),
 value: Array2::random((dim, dim), distribution),
 }
 }

 pub fn compute_attention(&self, x: &Array2<f32>) -> Array2<f32> {
 let q = x.dot(&self.query);
 let k = x.dot(&self.key);
 let v = x.dot(&self.value);

 let scores = q.dot(&k.t()) / (x.shape()[1] as f32).sqrt();
 let weights = scores.mapv(|s| s.exp()).sum_axis(Axis(1)).mapv(|sum| 1.0 / sum);
 weights.dot(&v)
 }
}
src/hof_cognition.rs
use ndarray::Array2;
use crate::flash_attention::FlashAttention;

pub struct HOF {
 attention: FlashAttention,
}

impl HOF {
 pub fn new(dim: usize) -> Self {
 HOF {
 attention: FlashAttention::new(dim),
 }
 }

 pub fn process(&self, input: &Array2<f32>) -> Array2<f32> {
 self.attention.compute_attention(input)
 }
}
src/distributed_representation.rs
use ndarray::Array2;
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use std::fs::File;
use std::io::{BufWriter, BufReader};

#[derive(Serialize, Deserialize)]
pub struct DistributedRepresenter {
 pub representations: HashMap<String, Array2<f32>>,
}

impl DistributedRepresenter {
 pub fn new() -> Self {
 DistributedRepresenter {
 representations: HashMap::new(),
 }
 }

 pub fn add_representation(&mut self, key: String, representation: Array2<f32>) {
 self.representations.insert(key, representation);
 }

 pub fn get_representation(&self, key: &str) -> Option<&Array2<f32>> {
 self.representations.get(key)
 }

 pub fn save(&self, path: &str) {
 let file = File::create(path).unwrap();
 let writer = BufWriter::new(file);
 serde_json::to_writer(writer, &self).unwrap();
 }

 pub fn load(path: &str) -> Self {
 let file = File::open(path).unwrap();
 let reader = BufReader::new(file);
 serde_json::from_reader(reader).unwrap()
 }
}
src/server.rs
use hyper::{Body, Request, Response, Server, Method, StatusCode};
use hyper::service::{make_service_fn, service_fn};
use ndarray::Array2;
use serde_json::json;
use std::sync::{Arc, Mutex};
use tokio::runtime::Runtime;

use crate::hof_cognition::HOF;
use crate::distributed_representation::DistributedRepresenter;

async fn handle_request(
 req: Request<Body>,
 hof: Arc<HOF>,
 representer: Arc<Mutex<DistributedRepresenter>>,
) -> Result<Response<Body>, hyper::Error> {
 match (req.method(), req.uri().path()) {
 (&Method::POST, "/process") => {
 let whole_body = hyper::body::to_bytes(req.into_body()).await?;
 let input_vec: Vec<f32> = serde_json::from_slice(&whole_body).unwrap();
 let input = Array2::from_shape_vec((1, input_vec.len()), input_vec).unwrap();
 let output = hof.process(&input);

 Ok(Response::new(Body::from(json!(output).to_string())))
 }
 (&Method::GET, "/retrieve") => {
 let representer = representer.lock().unwrap();
 if let Some(representation) = representer.get_representation("example") {
 Ok(Response::new(Body::from(json!(representation).to_string())))
 } else {
 let mut response = Response::new(Body::from("Representation not found"));
 *response.status_mut() = StatusCode::NOT_FOUND;
 Ok(response)
 }
 }
 _ => {
 let mut response = Response::new(Body::from("Not Found"));
 *response.status_mut() = StatusCode::NOT_FOUND;
 Ok(response)
 }
 }
}

pub fn run_server() {
 let hof = Arc::new(HOF::new(4));
 let representer = Arc::new(Mutex::new(DistributedRepresenter::new()));
 
 let make_svc = make_service_fn(|_conn| {
 let hof = hof.clone();
 let representer = representer.clone();
 async { Ok::<_, hyper::Error>(service_fn(move |req| handle_request(req, hof.clone(), representer.clone()))) }
 });

 let addr = ([127, 0, 0, 1], 3000).into();
 let server = Server::bind(&addr).serve(make_svc);

 println!("Server running on http://{}", addr);
 Runtime::new().unwrap().block_on(server).unwrap();
}
src/main.rs
mod flash_attention;
mod hof_cognition;
mod distributed_representation;
mod server;

use server::run_server;

fn main() {
 run_server();
}

ステップ3: サーバを起動する

Cargoを使ってサーバを起動します。

cargo run

Server running on http://127.0.0.1:3000というメッセージが表示されるはずです。

ステップ4: クライアント側のリクエストをテストする

curlを使ってサーバにHTTPリクエストを送ります。

処理のテスト:
curl -X POST http://localhost:3000/process -d '[0.5, 0.1, 0.4, 0.8]' -H "Content-Type: application/json"

処理された出力を含むJSONレスポンスを受け取るはずです。

検索のテスト:

まず、let hof = Arc::new(HOF::new(4));の後に以下を追加するようサーバのコードを修正し、表現がサーバに追加されていることを確認します。

let example_input = Array2::from_shape_vec((1, 4), vec![0.5, 0.1, 0.4, 0.8]).unwrap();
let example_output = hof.process(&example_input);
representer.lock().unwrap().add_representation("example".to_string(), example_output.clone());
representer.lock().unwrap().save("representations.json");

サーバを再起動し、次を実行します。

curl http://localhost:3000/retrieve

保存された表現を含むJSONレスポンスを受け取るはずです。

結論

この段階を追った手順では、Rustプロジェクトの構築、必要な構成要素の実装、サーバの起動、そしてクライアント側のHTTPリクエストによるテストを網羅しました。このアプローチは、現実世界のアプリケーションで最適な性能を得るためにFlash Attention機構をいかに活用するかを示し、Rustで先進的なAIシステムを開発するための包括的な枠組みを提供します。

関連記事: