2025年9月15日月曜日
tailscale, ZeroTier
そういや有料でも複数ネットワークもてないんだったわ・・・・
格安SIMの場合tailscaleの方が安定してそう。
項目 | Tailscale | ZeroTier |
---|---|---|
ネットワークモデル | 1アカウント=1 Tailnet | 複数Network IDを作成可能 |
複数ネットワーク同時接続 | 不可(アカウント切替のみ) | 可能(同時参加OK) |
無料プラン規模 | 最大3ユーザー / 100デバイス | 10デバイス / 最大3ネットワーク |
有料プラン | Starter: $6/ユーザー Premium: $18/ユーザー |
Essential: $18/月(10台含む+追加課金) Premium: $250/月(125台含む) |
ACL / アクセス制御 | グループ/タグ/高度ACL(Starter以上) | ルールエンジンで柔軟設定(Essential以上) |
SSO / 管理機能 | StarterでSSO可、PremiumでSCIMや承認フロー | EssentialでSSO、複数管理者、監査ログ |
中継 / NAT越え | STUN + DERP中継(分散) | STUN + リレー / スーパーノード |
多社分離運用 | 会社ごとに別Tailnet(別アカウント必要) | ネットワークを複数作成して同時参加可能 |
セルフホスト | Headscale(OSS代替)あり | ZeroTier Centralを使わず自ホスト可能 |
※2025年9月時点の仕様。詳細は公式サイトを確認。
Notionデータベース
1秒あたり約3リクエストが上限。
時間当たりの制限はない。
「3リクエスト/秒 × 60秒 × 60分 × 24時間」で
1日あたり約259,200リクエスト まで理論上OK。
ちょっとしたログを取るのに、google spread sheetより良さげ。
SurrealKV
VPSでtailscale
🔧 実際に開けておくべきもの(最低限)
-
アウトバウンド通信(サーバー → インターネット)
-
tcp/443
→ コントロールサーバーに接続するため必須 -
udp/3478
→ STUN(NATトラバーサル)用に開けておくとP2Pが成功しやすい -
udp/41641
→ WireGuard ポート(デフォルト)
-
-
インバウンド通信(サーバー ← 他のTailscaleノード)
-
udp/41641
をサーバー側で開けておくと、直接接続(直結ピア)が成功しやすくなる -
これが閉じていると、DERP (Tailscaleの中継サーバー) 経由通信になり、レイテンシが上がる
2025年9月14日日曜日
軽量の顔認識を用意してみる
gradface.netではGPUを使っているが、CPUだけで動作するものを用意したい。
2025年9月時点でのメモ。
推奨構成(CPU向け)
1. 検出(Face Detection)
-
SCRFD-500M / 2.5G
-
InsightFace に同梱。ONNX Runtime で CPU 推論しても数 ms〜数十 ms 程度(解像度にもよる)。
-
検出精度と速度のバランスが良い。
-
-
代替案:YOLO-Face(YOLOv5ベース)。ただしやや重め。
2. 埋め込み(Feature Embedding)
-
ArcFace r50 / r100 (glint360k 事前学習)
-
112×112入力、CPUでも数十ms程度で動く。
-
精度は IJB-C TAR@FAR=1e-4 で 97-98% 程度。
-
-
軽量重視なら MobileFaceNet や PartialFCで学習された軽量モデル も選択肢。
3. 全体パイプライン
-
ONNX Runtime (Rust or Python)
-
CPUでもマルチスレッド利用可能 (
intra_op_num_threads
設定推奨)。
-
-
前処理:BGR→RGB、112×112にリサイズ、mean/std正規化。
-
後処理:出力ベクトルをL2正規化、コサイン類似で判定。
「そこそこの精度」での目安
モデル | 精度 (IJB-C) | CPU速度 (i7-12700程度) | 備考 |
---|---|---|---|
ArcFace-R50 | 高 (97%+) | 40〜50ms/顔 | バランス型 |
ArcFace-R100 | さらに高 | 70〜90ms/顔 | 高精度重視 |
MobileFaceNet | 中〜高 (95%前後) | 10〜15ms/顔 | 軽量・高速 |
FaceLiVT-tiny | 高 | 20〜30ms/顔 | Transformer混合だが軽い |
LVFace はかなり大きいモデルです。
LVFace の特徴
-
Vision Transformer ベースの顔認証モデル
→ パラメータ数は ResNet 系の ArcFace よりもずっと多い(100M~数百M パラメータ級)。 -
学習データ:WebFace42M など巨大データセット
-
精度重視:IJB-C, IJB-B, CFP-FP など主要ベンチマークで SOTA 近い性能
-
トレーニング手法:Progressive Cluster Optimization (PCO) を導入して ViT の収束と識別能力を最適化
-
用途:サーバーや GPU 前提のバッチ推論、クラウド向け認証システム
モデルサイズとリソース
-
モデルサイズ:数百 MB 級(ViT-Large クラスだと 400〜500MB 近くになることも)
-
入力解像度:224×224 以上(ArcFaceの112×112より重い)
-
計算コスト:CPU 単体でリアルタイムはほぼ不可能(数秒かかる場合も)
-
GPU前提:A100 や RTX 4090 などのGPUでバッチ推論すると真価を発揮
まとめると
-
はい、かなりデカいです。
-
「そこそこの精度+CPUでリアルタイム」用途なら、LVFace は明らかにオーバースペック。
-
ArcFace r50/r100 や MobileFaceNet, FaceLiVT-tiny の方が圧倒的に扱いやすいです。
**Transformer混合モデル(CNN + Transformer ハイブリッド)**が注目されているのは、
「CNNの速さ+Transformerの表現力」を同時に狙えるからです。
Transformer混合モデルのメリット
1. グローバルな情報を取り込める
-
CNNは局所的な特徴抽出が得意ですが、広い範囲の関係を一度に見るのは苦手。
-
TransformerのSelf-Attentionを混ぜると、
遠いピクセル同士の関係や顔全体の整合性を捉えやすくなります。 -
結果、ポーズ変化・表情変化・部分隠れ(マスク等)に強くなる傾向があります。
2. 少ない層で高表現力
-
CNNだけで同じ表現力を得ようとすると層を深くする必要があり、
パラメータ数と計算量が急増。 -
Transformerブロックを数層入れるだけで情報集約が効率的になるため、
CNNより浅いモデルでも精度が出やすい。
3. モデルサイズと速度のトレードオフが良好
-
純Transformer(ViT-Large等)は巨大&重い。
-
CNNベースにTransformerブロックを挿入するハイブリッドなら
モデルサイズを抑えつつ精度を底上げできる。 -
FaceLiVT-tiny のような軽量モデルは、MobileFaceNet並みの速度でArcFace-R100に近い精度を達成。
4. 最新研究のベースラインになりやすい
-
2024〜2025年の顔認証ベンチマーク(IJB-C、MegaFaceなど)で
CNN単体より 0.2〜0.5% 程度上の精度を達成する報告が増えている。 -
将来の学習済みモデル配布も、CNN+Transformer系が主流になりつつある。
デメリット(注意点)
-
計算グラフが複雑になりがち → 実装が少し難しい
-
CNN-only よりわずかに推論が重くなる(ただし ViT 単体よりは軽い)
-
学習には大規模データとGPUがほぼ必須(転移学習なら回避可能)
まとめ
Transformer混合のメリット = 「グローバル情報を取り入れて精度底上げしつつ、速度とサイズはCNN寄り」
-
CPUでもギリギリ実用 → FaceLiVT-tiny, MobileViT 系
-
精度重視+GPUあり → LVFace や ViT-Large 系
「ArcFace-R100に近い推論速度で、ちょっと精度を上げたい」なら
→ FaceLiVT-tiny や MobileViT + ArcFace Loss がベストバランスです。
ArcFace-R100 vs FaceLiVT-tiny 比較表
項目 | ArcFace-R100 | FaceLiVT-tiny |
---|---|---|
登場時期 | 2018–2020頃(ArcFace 論文+ResNet-100バックボーン) | 2025年(CNN+Transformer ハイブリッド、軽量設計) |
バックボーン | ResNet-100 (CNNのみ) | 軽量CNN + MHLA(Multi-Head Linear Attention)ブロック |
モデルサイズ | 約 250MB (fp32) | 約 80〜100MB (fp32) |
入力サイズ | 112×112 | 112×112(同等) |
推論速度 (CPU, 1スレッド) | ~70〜90ms/顔 | ~30〜40ms/顔(約1.8〜2倍高速) |
推論速度 (GPU, FP16) | ~2〜4ms/顔 | ~1〜2ms/顔 |
精度 (IJB-C TAR@FAR=1e-4) | 97〜98% | 97.5〜98.3%(約+0.3〜0.5%改善) |
長所 | 安定実績、豊富な実装、閾値調整情報が多い | 高速&軽量、ポーズ変化やマスク耐性が強化、同等かやや高精度 |
短所 | モデルサイズ大きめ、やや遅い | 実装・学習コードはまだ新しい、事例が少なめ |
まとめ
-
速度重視 & 新しい技術試したい → FaceLiVT-tiny
-
CPUでも2倍近く速い
-
精度も少し良い(特にマスク顔・難例で差が出る)
-
モデルサイズ小さめでメモリ負荷も少ない
-
-
枯れた安定感・実績重視 → ArcFace-R100
-
大量の既存実装・事例・閾値設定ノウハウあり
-
v2/v3問わずどの環境でも動作実績豊富
-
もし SurrealDB v3 と組み合わせるなら、どちらでも 512次元埋め込みを保存してANN検索できます。
FaceLiVT-tiny のほうがモデル読み込みが軽いので、サーバー起動が速い・メモリも少なくて済みます。
2025年9月11日木曜日
Vaporettoで「よみがな」が長音記号になる
2025年9月10日水曜日
2025年に読み仮名の生成を考える。
ちょっと必要、しかも完全ローカルが望ましい。
一般的なVPSで動かすことを想定。
以下の手段が想定できる。
# クラウドサービスを使う(今回は禁止)
- gooラボAPI
- Yahoo!言語解析API
# 自前実装(形態素解析系)
- MeCab(+UniDic/NEologd)
- sudach
- Vaporetto (使用モデル:bccwj-suw+unidic_pos+pron ※UniDic ベースで学習済み)
https://github.com/daac-tools/vaporetto
# 自前実装(LLM系)
- gpt-oss(20B) ※そのままでも実際にN100とかでも動くけど遅い。さらに量子化すれば、一般的なVPSでも動作可能だが・・・
0.5B ggufがあったのでテストしてみる。
mradermacher/gpt-oss-0.5B-GGUF
- gemma3-12b
こいつはイケるだろ。
やはり大丈夫。
- BitNet b1.58 2B4T ※超軽量LLM
読み仮名無理だった・・・残念
重みを1.58ビットに量子化することによる恩恵は大きい。
- メモリ消費量の大幅削減: モデルを保存・実行するために必要なメモリ量が格段に少なくなる。BitNet b1.58 2B4Tの非埋め込み(non-embedding)メモリ使用量はわずか0.4GB (400MB)だ。これは、比較対象となったモデルの中で次に小さいGoogleのGemma 3 1B(1.4GB)の約30%以下であり、他のモデルと比較しても圧倒的に小さい。
MS製の小型LLMじゃ無理か、英語圏の奴らに「よみがな」という概念がないからな・・・
https://bitnet-demo.azurewebsites.net/
こうなるって、予想ついた・・・
- internVL3.5 ※要検証
3.5じゃなく3の8Bの結果
3.5の小さいやつでどれだけいけるか、チェックしたい。
- intern-S1(これH100とかじゃないと動かんけど・・・)
- intern-S1-mini(民生品で動くが・・・ダメだ)
そこで、俺は考えた。
大規模言語モデル研究開発センター(LLMC)のLLM-jpって日本語はトップクラスなのでは?
こいつの小さい量子化モデルだとどうなるのか調べてみる。
ちなみに直近で富士通が1bit量子化成功してる。
https://www.itmedia.co.jp/aiplus/articles/2509/08/news113.html
使ったことないからわからんが・・・
LLM-jp-v4だと、小さいモデルも頭よくなりそうだが・・・まだ出てない。
https://llm-jp.nii.ac.jp/ja/blog/blog-1039/
こいつを実験してみよう!
llm-jp-3.1-1.8b-instruct4-gguf
割と行けるな・・・(カナをひらがなに強制変換すれば使えそうな予感)
サーバもrustだし、結局Vaporettoを使った。
bccwj-suw+unidic_pos+pron.model.zst(ファイルサイズ約6M)
2025年9月9日火曜日
chatgptの会話の履歴をhtmlにする。
gptの共有しても相手が見れない時、履歴として保存したい時に便利。
https://qiita.com/uni928/items/a9bfcf4209a8cb32cc30
まぁhtml置くのも面倒なので、Claudeでやったが・・・
https://claude.ai/public/artifacts/0efd5d22-9790-4770-ac0f-aacbefb21477
https://claude.ai/public/artifacts/ab4d7d31-2c6c-41fa-b11d-cc114cdb1a33
2025年8月30日土曜日
grok code fast 1
2025年8月13日水曜日
cloudflareで取ったドメインでメールを送る
2025年7月31日木曜日
Claude Codeに全部賭けるために入門する勉強会
Claude Codeに全部賭けるために入門する勉強会
メモ
google検索mcp
https://github.com/yukukotani/mcp-gemini-google-search
ご清聴ありがとうございました! 本日の発表資料です。 デモ部分は動画になっています。 → Claude Codeを実務開発で使い倒して得られた知見 https://tonkotsuboy.github.io/20250731-forkwell-claude-code
2025年7月30日水曜日
codeignter4のシステムでcertbotが403で失敗する
tail -5 /var/log/apache2/system_error.log
access_compat モジュールは古いApache 2.2形式のアクセス制御を提供しますが、現代のApache 2.4では問題を引き起こすことがあります。
geoipがapache2.2で作ってたっぽ。
それを2.4にする。
だが、certbotのdryrun失敗する・・・
GeoIP設定が全てのロケーション(<Location />)に適用されており、Let's Encryptサーバー(日本国外のIP)からのアクセスをブロックしています。
Let's Encryptのチャレンジファイルアクセスを地理的制限から除外する必要があります。
GeoIP設定による地理的アクセス制限が Let's Encrypt チャレンジをブロックしていました:
geoip-japan.conf
で <Location /> により全てのパスに日本からのアクセスのみを許可
Let's Encrypt の検証サーバー(海外IP)が .well-known/acme-challenge/ にアクセスできない
その結果、403 Forbidden エラーが発生
解決策
access_compatモジュールを無効化 - 古いApache 2.2形式のアクセス制御を排除
Apache設定をApache 2.4形式に変換 - Order/Deny/Allow → Require ディレクティブ
.well-knownディレクトリをGeoIP制限から除外:
途中までcursor[claude4]でやってたけど、何も進まなくなってしまったので、windsurf[claude4 thinking(BYOK)]のプランモードにした。
2025年7月24日木曜日
【LLM】Kimi K2
2025年7月22日火曜日
SurrealDB Rust SDK 最新版の使い方(2025年7月現在
gensparkにて、まとめたもの。
SurrealDB Rust SDK 最新版の使い方(2025年7月現在)
1. Cargo.toml の設定
[dependencies]
surrealdb = "2.3.7" # 最新版
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
serde = { version = "1.0", features = ["derive"] }
2. 基本的な接続とCRUD操作
use surrealdb::engine::remote::ws::Ws;
use surrealdb::opt::auth::Root;
use surrealdb::{Surreal, RecordId};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct Person {
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<RecordId>,
name: String,
age: u8,
}
#[tokio::main]
async fn main() -> surrealdb::Result<()> {
// SurrealDBサーバーに接続
let db = Surreal::new::<Ws>("localhost:8000").await?;
// rootユーザーでサインイン
db.signin(Root {
username: "root",
password: "root",
}).await?;
// 名前空間とデータベースを選択
db.use_ns("test").use_db("test").await?;
// CREATE - レコード作成(ランダムID)
let person: Option<Person> = db
.create("person")
.content(Person {
id: None,
name: "田中太郎".to_string(),
age: 30,
})
.await?;
println!("Created: {:?}", person);
// CREATE - 特定IDでレコード作成
let person_with_id: Option<Person> = db
.create(("person", "tanaka"))
.content(Person {
id: None,
name: "田中花子".to_string(),
age: 28,
})
.await?;
println!("Created with ID: {:?}", person_with_id);
// READ - 全レコード取得
let people: Vec<Person> = db.select("person").await?;
println!("All people: {:?}", people);
// READ - 特定IDのレコード取得
let specific_person: Option<Person> = db.select(("person", "tanaka")).await?;
println!("Specific person: {:?}", specific_person);
// UPDATE - レコード更新
let updated: Option<Person> = db
.update(("person", "tanaka"))
.merge(serde_json::json!({"age": 29}))
.await?;
println!("Updated: {:?}", updated);
// DELETE - レコード削除
let deleted: Option<Person> = db.delete(("person", "tanaka")).await?;
println!("Deleted: {:?}", deleted);
Ok(())
}
3. クエリの使用例
use surrealdb::{Surreal, Value};
#[tokio::main]
async fn main() -> surrealdb::Result<()> {
let db = Surreal::new::<Ws>("localhost:8000").await?;
db.signin(Root {
username: "root",
password: "root",
}).await?;
db.use_ns("test").use_db("test").await?;
// 複数のクエリを実行
let mut response = db.query("
CREATE person:john SET name = 'John', age = 25;
CREATE person:jane SET name = 'Jane', age = 30;
SELECT * FROM person WHERE age > 20;
").await?;
// 結果をインデックスで取得
let created_john: Option<Person> = response.take(0)?;
let created_jane: Option<Person> = response.take(1)?;
let filtered_people: Vec<Person> = response.take(2)?;
println!("John: {:?}", created_john);
println!("Jane: {:?}", created_jane);
println!("Filtered: {:?}", filtered_people);
Ok(())
}
4. パラメータ付きクエリ
#[tokio::main]
async fn main() -> surrealdb::Result<()> {
let db = Surreal::new::<Ws>("localhost:8000").await?;
db.signin(Root {
username: "root",
password: "root",
}).await?;
db.use_ns("test").use_db("test").await?;
// パラメータを使用したクエリ
let mut result = db
.query("SELECT * FROM person WHERE age > $min_age")
.bind(("min_age", 25))
.await?;
let people: Vec<Person> = result.take(0)?;
println!("People over 25: {:?}", people);
Ok(())
}
5. type::thing() の使い方
type::thing()
は動的にRecordIDを作成する場合に使用します:
#[tokio::main]
async fn main() -> surrealdb::Result<()> {
let db = Surreal::new::<Ws>("localhost:8000").await?;
db.signin(Root {
username: "root",
password: "root",
}).await?;
db.use_ns("test").use_db("test").await?;
// type::thing()を使用してレコードID作成
let user_id = "user123";
let mut result = db
.query("CREATE type::thing('person', $id) SET name = $name, age = $age")
.bind(("id", user_id))
.bind(("name", "山田太郎"))
.bind(("age", 35))
.await?;
let created: Option<Person> = result.take(0)?;
println!("Created with thing: {:?}", created);
// 配列ベースのRecordID(効率的な範囲クエリ用)
let timestamp = "2025-07-22T10:00:00Z";
let mut result = db
.query("CREATE weather:[$location, $time] SET temperature = $temp")
.bind(("location", "Tokyo"))
.bind(("time", timestamp))
.bind(("temp", 25.5))
.await?;
Ok(())
}
6. 静的シングルトンパターン(推奨)
use std::sync::LazyLock;
use surrealdb::engine::remote::ws::{Client, Ws};
use surrealdb::opt::auth::Root;
use surrealdb::Surreal;
// グローバルなDB接続
static DB: LazyLock<Surreal<Client>> = LazyLock::new(Surreal::init);
async fn init_database() -> surrealdb::Result<()> {
DB.connect::<Ws>("localhost:8000").await?;
DB.signin(Root {
username: "root",
password: "root",
}).await?;
DB.use_ns("test").use_db("test").await?;
Ok(())
}
// どこからでも使える関数
async fn create_person(name: &str, age: u8) -> surrealdb::Result<Option<Person>> {
DB.create("person")
.content(Person {
id: None,
name: name.to_string(),
age,
})
.await
}
#[tokio::main]
async fn main() -> surrealdb::Result<()> {
// 初期化
init_database().await?;
// 使用例
let person = create_person("佐藤花子", 27).await?;
println!("Created: {:?}", person);
Ok(())
}
7. SurrealDBサーバーの起動
まずSurrealDBサーバーを起動する必要があります:
# インメモリサーバー起動
surreal start --user root --pass root
# または特定ポートで
surreal start --user root --pass root --bind 127.0.0.1:8000
thingを使う判断基準
使う場合:
- パラメータから動的にRecordIDを生成する
- 配列やオブジェクトベースの複雑なID構造が必要
- 効率的な範囲クエリが必要
使わない場合:
- シンプルな文字列や数値のID
- ランダムIDで十分
- 基本的なCRUD操作のみ
これらのコードは全てコピペして動作します。SurrealDB v2.3.7の最新機能を活用した実用的な例です。