hotchpotch commited on
Commit
6597b2f
·
verified ·
1 Parent(s): 2b3a7e0

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +230 -56
README.md CHANGED
@@ -23,70 +23,244 @@ datasets:
23
  library_name: sentence-transformers
24
  ---
25
 
26
- ### Framework Versions
27
- - Python: 3.12.7
28
- - Sentence Transformers: 3.3.1
29
- - Transformers: 4.48.0
30
- - PyTorch: 2.5.1+cu124
31
- - Accelerate: 1.3.0
32
- - Datasets: 3.2.0
33
- - Tokenizers: 0.21.0
34
-
35
- ## Citation
36
-
37
- ### BibTeX
38
-
39
- #### Sentence Transformers
40
- ```bibtex
41
- @inproceedings{reimers-2019-sentence-bert,
42
- title = "Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks",
43
- author = "Reimers, Nils and Gurevych, Iryna",
44
- booktitle = "Proceedings of the 2019 Conference on Empirical Methods in Natural Language Processing",
45
- month = "11",
46
- year = "2019",
47
- publisher = "Association for Computational Linguistics",
48
- url = "https://arxiv.org/abs/1908.10084",
49
- }
 
 
 
50
  ```
 
 
 
 
 
51
 
52
- #### MatryoshkaLoss
53
- ```bibtex
54
- @misc{kusupati2024matryoshka,
55
- title={Matryoshka Representation Learning},
56
- author={Aditya Kusupati and Gantavya Bhatt and Aniket Rege and Matthew Wallingford and Aditya Sinha and Vivek Ramanujan and William Howard-Snyder and Kaifeng Chen and Sham Kakade and Prateek Jain and Ali Farhadi},
57
- year={2024},
58
- eprint={2205.13147},
59
- archivePrefix={arXiv},
60
- primaryClass={cs.LG}
61
- }
 
 
 
 
 
 
62
  ```
63
 
64
- #### MultipleNegativesRankingLoss
65
- ```bibtex
66
- @misc{henderson2017efficient,
67
- title={Efficient Natural Language Response Suggestion for Smart Reply},
68
- author={Matthew Henderson and Rami Al-Rfou and Brian Strope and Yun-hsuan Sung and Laszlo Lukacs and Ruiqi Guo and Sanjiv Kumar and Balint Miklos and Ray Kurzweil},
69
- year={2017},
70
- eprint={1705.00652},
71
- archivePrefix={arXiv},
72
- primaryClass={cs.CL}
73
- }
74
  ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
- <!--
77
- ## Glossary
78
 
79
- *Clearly define terms in order to be accessible across audiences.*
80
- -->
81
 
82
- <!--
83
- ## Model Card Authors
84
 
85
- *Lists the people who create the model card, providing recognition and accountability for the detailed work that goes into its construction.*
86
- -->
87
 
88
- <!--
89
- ## Model Card Contact
90
 
91
- *Provides a way for people who have updates to the Model Card, suggestions, or questions, to contact the Model Card authors.*
92
- -->
 
23
  library_name: sentence-transformers
24
  ---
25
 
26
+ 以下の文章は、ブログ記事⭐️からの転載です。
27
+
28
+ # 100倍速で実用的な文章ベクトルを作れる、日本語 StaticEmbedding を公開
29
+
30
+ 文章の密ベクトルは、情報検索・文章判別・類似文章抽出など、さまざまな用途に使うことができます。しかしながら最先端のTransformerモデルは小さいモデルでも、とりわけCPUでは遅く、変換速度が実用でないこともしばしばです。
31
+
32
+ しかしながら、先日公開されたTransformerモデル「ではない」 [StaticEmbedding](https://huggingface.co/blog/static-embeddings)は、例えば [intfloat/multilingual-e5-small](https://huggingface.co/intfloat/multilingual-e5-small) (以下mE5-small)とのベンチマーク比較では85%のスコアという実用できる性能で、かつCPUで動作時に126倍高速に文ベクトルを作成することができる、という驚きの速度です。
33
+
34
+ というわけで、早速日本語(と英語)で学習させたモデル sentence-embedding-japanese を作成し、公開しました。
35
+
36
+ - https://huggingface.co/hotchpotch/static-embedding-japanese
37
+
38
+ 日本語の文章ベクトルの性能を評価する JMTEB の結果は以下です。確かに mE5-small には若干及ばないまでも、タスクによっては勝っていたりしますし、[他の日本語baseサイズbertモデルよりもスコアが高いこともある](https://github.com/sbintuitions/JMTEB/blob/main/leaderboard.md)ぐらい、最低限実用に達している性能が出ていますね。本当にそんなに性能出るのか実際に学習させてみるまで半信半疑でしたが、すごいですね。
39
+
40
+ | Model | Avg(micro) | Retrieval | STS | Classification | Reranking | Clustering | PairClassification |
41
+ | ---------------------------------------- | ---------- | --------- | ----- | -------------- | --------- | ---------- | ------------------ |
42
+ | text-embedding-3-small | 69.18 | 66.39 | 79.46 | 73.06 | 92.92 | 51.06 | 62.27 |
43
+ | multilingual-e5-small | 67.71 | 67.27 | 80.07 | 67.62 | 93.03 | 46.91 | 62.19 |
44
+ | **static-embedding-japanese** | 66.66 | **67.92** | **80.16** | **67.96** | 91.87 | 35.83 | **62.37** |
45
+
46
+
47
+ なお、StaticEmbedding 日本語モデル学習などの技術的なことは記事の後半に書いているので、興味がある方はどうぞ。
48
+
49
+ ## 利用方法
50
+
51
+ 利用は簡単、SentenceTransformer を使っていつもの方法で文ベクトルを作れます。今回はGPUを使わず、CPUで実行してみましょう。なお SentenceTransformer は 3.3.1 で試しています。
52
+
53
  ```
54
+ pip install "sentence-transformers>=3.3.1"
55
+ ```
56
+
57
+ ```python
58
+ from sentence_transformers import SentenceTransformer
59
 
60
+ model_name = "hotchpotch/static-embedding-japanese"
61
+ model = SentenceTransformer(model_name, device="cpu")
62
+
63
+ query = "美味しいラーメン屋に行きたい"
64
+ docs = [
65
+ "素敵なカフェが近所にあるよ。落ち着いた雰囲気でゆっくりできるし、窓際の席からは公園の景色も見えるんだ。",
66
+ "新鮮な魚介を提供する店です。地元の漁師から直接仕入れているので鮮度は抜群ですし、料理人の腕も確かです。",
67
+ "あそこは行きにくいけど、隠れた豚骨の名店だよ。スープが最高だし、麺の硬さも好み。",
68
+ "おすすめの中華そばの店を教えてあげる。とりわけチャーシューが手作りで柔らかくてジューシーなんだ。",
69
+ ]
70
+
71
+ embeddings = model.encode([query] + docs)
72
+ print(embeddings.shape)
73
+ similarities = model.similarity(embeddings[0], embeddings[1:])
74
+ for i, similarity in enumerate(similarities[0].tolist()):
75
+ print(f"{similarity:.04f}: {docs[i]}")
76
  ```
77
 
 
 
 
 
 
 
 
 
 
 
78
  ```
79
+ (5, 1024)
80
+ 0.1040: 素敵なカフェが近所にあるよ。落ち着いた雰囲気でゆっくりできるし、窓際の席からは公園の景色も見えるんだ。
81
+ 0.2521: 新鮮な魚介を提供する店です。地元の漁師から直接仕入れているので鮮度は抜群ですし、料理人の腕も確かです。
82
+ 0.4835: あそこは行きにくいけど、隠れた豚骨の名店だよ。スープが最高だし、麺の硬さも好み。
83
+ 0.3199: おすすめの中華そばの店を教えてあげる。とりわけチャーシューが手作りで柔らかくてジューシーなんだ。
84
+ ```
85
+
86
+ このように、queryにマッチする文章のスコアが高くなるように計算できてますね。この例文では、例えばBM25ではqueryに含まれる「ラーメン」のような直接的な単語が文章に出ていないため、うまくマッチさせることが難しいでしょう。
87
+
88
+ また速度も、CPUで文ベクトルを作った方は少ない文章量でもだいぶ時間がかかるな、という経験をされた方も多いと思いますが、StaticEmbedding モデルではCPUがそこそこ速ければ一瞬で終わると思います。さすが100倍速。
89
+
90
+ ## なぜCPUで推論が高速なの?
91
+
92
+ StaticEmbedding はTransformerモデルではありません。つまりTrasformerの特徴であるアテンションの計算が一切ないです。文章に出てくる単語トークンを1024次元のテーブルに保存して、文ベクトルではそれの平均をとっているだけです。なお、アテンションがないので、文脈の理解などはしていません。
93
+
94
+ また PyTorch の nn.EmbeddingBag を使って、全てを連結したトークンとオフセットを渡して処理することで、PyTorch の最適化で高速なCPU並列処理とメモリアクセスがされているようです。
95
+
96
+ ![](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/static-embeddings/similarity_speed.png)
97
+
98
+ [元記事の速度評価結果によると](https://huggingface.co/blog/static-embeddings#multilingual-similarity-4)CPUではmE5-smallと比べて126倍速らしいですね。
99
+
100
+ ## 評価結果
101
+
102
+ JMTEBでの全ての評価結果は[こちらJSONファイルに記載](https://huggingface.co/hotchpotch/static-embedding-japanese/blob/main/JMTEB/summary.json)しています。[JMTEB Leaderboard](https://github.com/sbintuitions/JMTEB/blob/main/leaderboard.md)で見比べると、差がわかるでしょう。JMTEBの全体の評価結果はモデルサイズを考えると、すこぶる良好です。なお、JMTEB で評価された方は、mr-tidy タスクの700万文章のベクトル化に時間がかなりかかる(モデルにもよりますがRTX4090で1~4時間ほど)と思います。これもStaticEmbeddingsでは非常に速く、RTX4090では約4分で処理終えることができました。
103
+
104
+ ### 情報検索でBM25の置き換えができそうか?
105
+
106
+ JMTEBの中の情報検索タスクの[Retrievalの結果](https://huggingface.co/hotchpotch/static-embedding-japanese/blob/main/JMTEB/summary.json)を見てみましょう。StaticEmbedding では mr-tidy の項目が著しく悪いですね。mr-tidyは他のタスクに比べて文章量が圧倒的に多く(700万文章)、つまる所大量の文章を検索するようなタスクでは結果が悪い可能性がありそうです。文脈を無視したた単純なトークンの平均なので、増えれば増えるほど似た平均の文章が出てくるとすると、そういう結果にもなり得そうですね。
107
+
108
+ ので、大量の文章の場合、BM25よりもだいぶ性能が悪い可能性がありそうです。ただ、少ない文章で、ずばりの単語マッチが少ない場合は、BM25よりも良好な結果になることが多そうですね。
109
+
110
+ なお情報検索タスクの jaqket の結果が他のモデルに対してやたら良いのは、JQaRa (dev, unused)を学習しているからといっても高すぎる感じで謎です。test の情報リークはしていないとは思うのですが…。
111
+
112
+ ### クラスタリング結果が悪い
113
+
114
+ こちらも詳細は追っかけていませんが、スコア的には他のモデルよりもだいぶ悪い結果ですね。クラス分類タスクは悪くないので不思議です。埋め込み空間がマトリョーシカ表現学習で作られた影響もあるのでしょうか。
115
+
116
+ ## JQaRA, JaCWIR でのリランキングタスク
117
+
118
+ [JQaRA](https://huggingface.co/datasets/hotchpotch/JQaRA) の結果はこちら。
119
+
120
+ | model_names | ndcg@10 | mrr@10 |
121
+ |:-----------------------------------------------------------------------------------------|----------:|---------:|
122
+ | [static-embedding-japanese](https://huggingface.co/hotchpotch/static-embedding-japanese) | 0.4704 | 0.6814 |
123
+ | bm25 | 0.458 | 0.702 |
124
+ | [multilingual-e5-small](https://huggingface.co/intfloat/multilingual-e5-small) | 0.4917 | 0.7291 |
125
+
126
+ [JaCWIR](https://huggingface.co/datasets/hotchpotch/JaCWIR) の結果はこちら。
127
+
128
+ | model_names | map@10 | hits@10 |
129
+ |:-----------------------------------------------------------------------------------------|---------:|----------:|
130
+ | [static-embedding-japanese](https://huggingface.co/hotchpotch/static-embedding-japanese) | 0.7642 | 0.9266 |
131
+ | bm25 | 0.8408 | 0.9528 |
132
+ | [multilingual-e5-small](https://huggingface.co/intfloat/multilingual-e5-small) | 0.869 | 0.97 |
133
+
134
+ JQaRa 評価は BM25 よりは若干良く、mE5-small よりは若干低い、JaCWIR は BM25, mE5よりだいぶ低い感じの結果になりました。
135
+
136
+ JaCWIR はWeb文章のタイトルと概要文なので、いわゆる「綺麗な」文章ではないケースも多く、transformerモデルはノイズに強いので、単純なトークン平均のStaticEmbeddingでは悪い結果になりそうです。BM25は特徴的な単語にマッチしやすいので、JaCWIR でもノイズとなるような単語はクエリにマッチしないため、Transformer モデルと競争力のある結構良い結果を残します。
137
+
138
+ この結果から、StaticEmbedding は Transformer / BM25 に比べ、ノイズを多く含む文章の場合はスコアが悪い可能性があります。
139
+
140
+ ## 出力次元の削減
141
+
142
+ StaticEmbedding で出力される次元は、学習次第ですが今回作成したものは1024次元とそこそこのサイズです。次元数が大きいと、推論後のタスク(クラスタリングや情報検索など)に計算コストがかかってしまいます。しかしながら、学習時にマトリョーシカ表現学習([Matryoshka Representation Learning(MRL)](https://arxiv.org/abs/2205.13147))をしているため、1024次元をさらに小さな次元へと簡単に次元削減ができます。
143
+
144
+ MRLは、学習時に先頭のベクトルほど重要な次元を持ってくることで、例えば1024次元でも先頭の32,64,128,256...次元だけを使って後ろを切り捨てるだけで、ある程度良好な結果を示しています。
145
+
146
+ ![](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/static-embeddings/nano_beir_matryoshka.png)
147
+
148
+ このグラフ参照元の[StaticEmbedding の記事](https://huggingface.co/blog/static-embeddings#matryoshka-evaluation)によると、128次元で91.87%, 256次元で95.79%, 512次元で98.53%の性能を維持しているようです。精度にそこまでシビアではないが、その後の計算コストを下げたい場合、ガッと次元削減して使う、という用途にも使えそうですね。
149
+
150
+ ## StaticEmbedding モデルを作ってみて
151
+
152
+ 正直、単純なトークンのembeddingsの平均でそんなに性能出るのか半信半疑だったのですが、実際に学習させてみてシンプルなアーキテクチャなのに性能の高さにびっくりしました。Transformer 全盛のこの時代に、古き良き単語埋め込みの活用モデルで、実世界で利活用できそうなモデルの出現に驚きを隠せません。
153
+
154
+ CPUでの推論速度が速い文ベクトル作成モデルは、ローカルCPU環境で大量の文章の変換などはもとより、エッジデバイスだったりネットワークが遅い(リモートの推論サーバを叩けない)環境だったり、色々と活用しがいがありそうですね。
155
+
156
+ ---
157
+
158
+ # StaticEmbedding 日本語モデル学習のテクニカルノート
159
+
160
+
161
+ ## なぜうまく学習できるのか
162
+
163
+ StaticEmbedding は非常にシンプルで、文章をトークナイズしたIDで単語の埋め込みベクトルが格納されているEmbeddingBagテーブルからN次元(今回は1024次元)のベクトルを取得し、その平均を取るだけです。
164
+
165
+ これまで、単語埋め込みベクトルといえば、word2vec や GloVe のように Skip-gram や CBOW を用いて単語の周辺を学習してきました。しかし、StaticEmbedding では文章全体を用いて学習しています。また、対照学習を使って大量の文章を巨大バッチで学習しており、良い単語の埋め込み表現の学習に成功しているようです。
166
+
167
+ ## 学習データセット
168
+
169
+ 日本語モデル学習にあたり、対照学習で利用���きるデータセットとして、以下を作成し使用しました。
170
+
171
+ - [hotchpotch/sentence_transformer_japanese](https://huggingface.co/datasets/hotchpotch/sentence_transformer_japanese)
172
+ - [SentenceTransformer で学習しやすいカラム名と構造](https://sbert.net/docs/sentence_transformer/loss_overview.html)に整えたものです。
173
+ - `(anchor, positive)`, `(anchor, positive, negative)`, `(anchor, positive, negative_1, ..., negative_n)` といった構造になっています。
174
+ - 以下のデータセットを基に hotchpotch/sentence_transformer_japanese を作成しました。毎度ながらデータセットの作者の方々・とりわけ hpprc 氏に感謝です。
175
+ - https://huggingface.co/datasets/hpprc/emb
176
+ - https://huggingface.co/datasets/hotchpotch/hpprc_emb-scores のリランカースコアを使用し、positive(>=0.7) / negative(<=0.3) のフィルタリングを行いました。
177
+ - https://huggingface.co/datasets/hpprc/llmjp-kaken
178
+ - https://huggingface.co/datasets/hpprc/msmarco-ja
179
+ - [https://huggingface.co/datasets/hotchpotch/msmarco-ja-hard-negatives](https://huggingface.co/datasets/hotchpotch/msmarco-ja-hard-negatives) のリランカースコアを用いて、positive(>=0.7) / negative(<=0.3) のフィルタリングを行いました。
180
+ - https://huggingface.co/datasets/hpprc/mqa-ja
181
+ - https://huggingface.co/datasets/hpprc/llmjp-warp-html
182
+ - 上記の作成したデータセットの中で、以下を使用しました。なお、情報検索を強化したかったため、情報検索に適したデータセットのデータはオーギュメンテーションで件数を多めに学習させています。
183
+ - httprc_auto-wiki-nli-triplet
184
+ - httprc_auto-wiki-qa
185
+ - httprc_auto-wiki-qa-nemotron
186
+ - httprc_auto-wiki-qa-pair
187
+ - httprc_baobab-wiki-retrieval
188
+ - httprc_janli-triplet
189
+ - httprc_jaquad
190
+ - httprc_jqara
191
+ - httprc_jsnli-triplet
192
+ - httprc_jsquad
193
+ - httprc_miracl
194
+ - httprc_mkqa
195
+ - httprc_mkqa-triplet
196
+ - httprc_mr-tydi
197
+ - httprc_nu-mnli-triplet
198
+ - httprc_nu-snli-triplet
199
+ - httprc_quiz-no-mori
200
+ - httprc_quiz-works
201
+ - httprc_snow-triplet
202
+ - httprc_llmjp-kaken
203
+ - httprc_llmjp_warp_html
204
+ - httprc_mqa_ja
205
+ - httprc_msmarco_ja
206
+ - 英語データセットには、以下のデータセットを利用しています。
207
+ - [sentence-transformers/msmarco-co-condenser-margin-mse-sym-mnrl-mean-v1](https://huggingface.co/datasets/sentence-transformers/msmarco-co-condenser-margin-mse-sym-mnrl-mean-v1)
208
+ - [sentence-transformers/squad](https://huggingface.co/datasets/sentence-transformers/squad)
209
+ - [sentence-transformers/all-nli](https://huggingface.co/datasets/sentence-transformers/all-nli)
210
+ - [sentence-transformers/trivia-qa](https://huggingface.co/datasets/sentence-transformers/trivia-qa)
211
+ - [nthakur/swim-ir-monolingual](https://huggingface.co/datasets/nthakur/swim-ir-monolingual)
212
+ - [sentence-transformers/miracl](https://huggingface.co/datasets/sentence-transformers/miracl)
213
+ - [sentence-transformers/mr-tydi](https://huggingface.co/datasets/sentence-transformers/mr-tydi)
214
+
215
+
216
+ ## 日本語トークナイザ
217
+
218
+ StaticEmbedding を学習するためには、HuggingFace のトークナイザライブラリの tokenizer.json 形式で処理可能なトークナイザを使うと簡単そうだったので、 [hotchpotch/xlm-roberta-japanese-tokenizer](https://huggingface.co/hotchpotch/xlm-roberta-japanese-tokenizer) というトークナイザを作成しました。語彙数は 32,768 です。
219
+
220
+ このトークナイザは、wikipedia 日本語、wikipedia 英語(サンプリング)、cc-100(日本語, サンプリング)のデータを unidic で分割し、sentencepiece unigram で学習したものです。XLM-Roberta 形式の日本語トークナイザとしても機能します。今回はこのトークナイザを利用しました。
221
+
222
+ ## ハイパーパラメータ
223
+
224
+ [大元の学習コード](https://huggingface.co/blog/static-embeddings#english-retrieval-2)との変更点やメモは以下の通りです。
225
+
226
+ - batch_size を大元の 2048 から 6072 に設定しました。
227
+ - 対照学習で巨大なバッチを処理するとき、同一バッチ内にポジティブとネガティブが含まれると学習に悪影響を与える可能性があります。これを防ぐために [BatchSamplers.NO_DUPLICATES](https://sbert.net/docs/package_reference/sentence_transformer/sampler.html) オプションがあります。しかし、バッチサイズが巨大だと同一バッチに含めないためのサンプリング処理に時間がかかることがあります。
228
+ - 今回は `BatchSamplers.NO_DUPLICATES` を指定し、RTX4090 の 24GB に収まる 6072 に設定しました。バッチサイズはさらに大きい方が結果が良い可能性があります。
229
+ - epoch数を1から2に変更しました
230
+ - 1よりも2の方が良い結果になりました。ただし、データサイズがもっと大きければ、1の方が良い可能性があります。
231
+ - スケジューラ
232
+ - 標準のlinearから、経験則でより良いと感じるcosineに変更しました。
233
+ - オプティマイザ
234
+ - 標準のAdamW のままです。adafactorに変更した場合、収束が悪くなりました。
235
+ - learning_rate
236
+ - 2e-1 のままです。値が巨大すぎるのではないかと疑問に思いましたが、低くすると結果が悪化しました。
237
+ - dataloader_prefetch_factor=4
238
+ - dataloader_num_workers=15
239
+ - トークナイズとバッチサンプラのサンプリングに時間がかかるため、大きめに設定しました。
240
+
241
+ ## 学習リソース
242
+
243
+ - CPU
244
+ - Ryzen9 7950X
245
+ - GPU
246
+ - RTX4090
247
+ - memory
248
+ - 64GB
249
+
250
+ このマシンリソースでの学習にかかった時間は約4時間でした。GPUのコア負荷は非常に小さく、他のtransformerモデルでは学習時に90%前後で張り付くのに対して、StaticEmbeddingではほとんど0%でした。これは、巨大なバッチをGPUメモリに転送する時間が大半を占めているためかと思われます。そのため、GPUメモリの帯域幅が速くなれば、学習速度がさらに向上する可能性があります。
251
+
252
+ ## さらなる性能向上へ
253
+
254
+ 今回利用したトークナイザはStaticEmbedding向けに特化したものではないため、より適したトークナイザを使用すれば性能が向上する可能性があります。バッチサイズをさらに巨大化することで、学習の安定性が向上し、性能向上が見込めるかもしれません。
255
 
256
+ また、さまざまなドメインや合成データセットを利用するなど、より幅広い文章リソースを学習に組み込むことで、さらなる性能向上が期待できます。
 
257
 
258
+ ## 大元の学習コード
 
259
 
260
+ 学習に使用したコードは、以下で MIT ライセンスで公開しています。スクリプトを実行すれば再現できる、はず...!
 
261
 
262
+ - https://huggingface.co/hotchpotch/static-embedding-japanese/blob/main/trainer.py
 
263
 
264
+ ## ライセンス
 
265
 
266
+ static-embedding-japanese MIT ライセンスで公開しています。