#author("2026-02-20T07:32:29+01:00","","") #author("2026-02-20T07:35:14+01:00","","") [[技術資料]] 大規模言語モデルに組み込む動的適応プルーニングの提案手法 *目次 [#e890b3d5] #CONTENTS *目的 [#x38aa8d3] 莫大なパラメータを保持するLLMは、推論時間や消費電力に課題点がある。 理由として、不必要なパラメータも計算リソースとして含まれるからである。 そのため、本研究の目的はそのような不必要なパラメータを削除し、軽量なモデルの作成を目指す。 *使うモジュールのインストール [#u1248302] ***使用するモジュール [#ta19c6d2] |モジュール|version|用途| |torch||PyTorchはLlama2のような大規模言語モデルのトレーニングに必須| |transformers||モデルの読み込み、トレーニング、評価| |datasets||データセットのロードと前処理| |accelerate||分散学習や混合精度トレーニング| |bitsandbytes||量子化トレーニングや8ビット最適化に利用.| |scipy||統計的な操作や最適化に利用| |optimum|| Hugging Faceのエコシステムで、モデルの最適化(プルーニング、量子化など)をサポート| モジュールのインストールはコマンドプロンプトでpip install モジュール名 バージョンまで指定する場合はコマンドプロンプトでpip install モジュール名==指定するバージョン でインストールする *事前学習モデル、データセットのダウンロード [#ob3ca958] 本研究では、手動でのデータ収集(スクレイピング等)や事前ダウンロードは行わない。 Pythonの transformers および datasets ライブラリの機能(from_pretrained および load_dataset)を用いることで、プログラム実行時にHugging Face Hubから自動的にキャッシュディレクトリへダウンロードおよび読み込みが行われる仕様となっている。 ※なお、使用するGPT-2およびWikiText-2はパブリック公開されているため、Hugging FaceのAPIアクセストークンによる認証プロセスは不要である。 ***Hugging Face Hubとは [#ta19c6d2] 自然言語処理(NLP)や生成AI分野を中心に、世界中のAIモデル、データセット、ツールを共有・利用できる世界最大級のオープンソースAIプラットフォーム ***Transformer [#ta19c6d2] 2017年にGoogleの研究者らによって発表された、ニューラルネットワークの一種です。元々は機会翻訳などの系列変換タスクを想定して開発されましたが、その後ChatGPTのベースとなる技術として大きな注目を集めました。 Transformerの大きな特徴は、「Attention Mechanism」を主体に処理を行う点にあります。従来のニューラルネットワークとは異なるアプローチにより、高い精度と効率性を重視しています。 ***モデルのダウンロード方法 [#ta19c6d2] 今回はGPT-2というモデルを採用した。 transformersモジュール経由で自動ダウンロード。 from transformers import AutoModelForCausalLM, AutoTokenizer # ここでモデルとトークナイザーをダウンロード&読み込み tokenizer = AutoTokenizer.from_pretrained("gpt2") model = AutoModelForCausalLM.from_pretrained("gpt2") このコードでは「Hugging Face Hub」からGPT-2モデルをGoogle Colab(またはローカルPC)の「キャッシュフォルダ」に暗黙的に保存される。そのため、2回目以降の実行ではダウンロードなしで素早く読み込まれます。 ***データセットのダウンロード方法 [#ta19c6d2] 今回はwikipediaのデータセットを使用した。 datasetsモジュール経由で自動ダウンロードされています。 from datasets import load_dataset # ここでデータセットをダウンロード&読み込み dataset = load_dataset("wikitext", "wikitext-2-raw-v1") Hugging Face のサーバーからWikiText-2のテキストデータがダウンロードされる。 **APIを用いたモデルのダウンロード [#xbcf3848] ・API作成 hugging face(URL:https://huggingface.co/)のwebページでアカウント登録をする。登録後、右上のアイコンをクリックし、access Tokenをクリック。その後、Create APIより作成。 *実験手法と実行手順 [#experimental_methods] 本研究では、提案手法の有効性を検証するため、「ベースライン」「AGIP」「AGIP+TA」の3つのアプローチで実験・比較を行う。 以下に各手法の目的、特徴、および実行方法をまとめる。 ・API作成後モデルのダウンロード **ベースライン(従来手法)の実行 [#baseline] ベースライン手法では、高度なプルーニング制御や特殊な更新式を用いず、標準的な学習および単純なプルーニングを実施する。 実行環境にて下記のコマンドを入力にて huggingface-cli login 右クリックで貼り付け 分析するためにはデータの前処理を行わなければ正しい結果は得られない. 今回は前処理の手法として正規化を行う. 正規化の手法として,VAR-LiNGAMではMin-Max法によるデータの正規化,RF5に用いるデータの正規化手法としては説明変数にMax法による正規化,目的変数にはZ scoreによる正規化を行う. *VAR-LiNGAMへの適用 [#eb3cce83] VAR-LiNGAMの分析ではPythonのモジュール「LiNGAM」のVAR-LiNGAMを用いる. これはhttps://lingam.readthedocs.io/en/latest/tutorial/var.html を参考にすればよい path = "用いるデータのパス" df=pd.read_csv(path,parse_dates=True,index_col="Day",encoding="cp932") scaler = MinMaxScaler() normalized_df = pd.DataFrame(scaler.fit_transform(df), columns=df.columns) model = lingam.VARLiNGAM(lags=2, prune=True) model.fit(normalized_df) 上のコードではデータの読み取り,正規化を行い,それについてVAR-LiNGAMを実行している. labels = [f'{col}(t)' for i, col in enumerate(normalized_df.columns)]+[f'{col}(t-1)' for i,col in enumerate(df.columns)] make_dot(np.hstack(model.adjacency_matrices_), lower_limit=0.05, ignore_shape=True, labels=labels ) 上のコードで2次元グラフを作成している.このコードは結果を出すうえで特に必要ないため,実行しなくても良いが必要になった場合にはgraphvizのインストールが必要になる. graphvizのインストールについては以下のサイトを参考にした 目的: 提案手法(AGIPやTA)を評価するための基準(ベンチマーク)スコアを作成する。 https://www.kkaneko.jp/tools/win/graphviz.html 特徴: 標準的なオプティマイザ(AdamW等)のみを用いてファインチューニングを行うか、あるいは固定率で一度に重みを削除する単純なマグニチュード・プルーニングを行う。 Lossに応じた自動調整機能や、平坦な地形での収束を早める機能は持たない。 model = lingam.VARLiNGAM() result = model.bootstrap(normalized_df,n_sampling=100) これでLiNGAMのVAR-LiNGAMをモデルとして設定し,ブートストラップ法による分析を行っている.n_samplingの数でサンプリング回数を設定し,今回は100回になっているが,用いるデータのサンプル数を考慮し設定する必要がある. ブートストラップ法では一般的に用いるデータのサンプル数よりも大きいものを設定することが望ましい. p_values = model.get_error_independence_p_values() これで誤差変数間の独立性を分析する. cdc = result.get_causal_direction_counts(n_directions=8, min_causal_effect=0.3, split_by_causal_effect_sign=True) このコードで因果関係の方向性を取得する.n_directionは上位いくつの因果方向を限定するかを設定するものであり,min_causal_effectでは係数が何以上の因果方向に限定するかを指定できる. print_causal_directions(cdc, 100, labels=labels) でその結果を描画できる. dagc = result.get_directed_acyclic_graph_counts(n_dags=3, min_causal_effect=0.2, split_by_causal_effect_sign=True) これではDAGのランキングを取得できる.また, causal_effects = result.get_total_causal_effects(min_causal_effect=0.01) df = pd.DataFrame(causal_effects) 実行手順: ベースライン実行用のノートブック(または該当セル)を実行し、学習完了後の最終的なLossおよびPerplexity(PPL)の値を記録する。 df['from'] = df['from'].apply(lambda x : labels[x]) df['to'] = df['to'].apply(lambda x : labels[x]) df これで合計因果関係を取得することができ,この結果を用いて3Dグラフの作成を行うため,pandasのto_csvを用いることでcsvファイルに保存しておく. #ref(ingakekka.PNG) 得られた因果性は上のような形式で保存される. fromには影響を与えている要素,toには影響を与えられている要素,valueには因果性の大きさが格納されている. 一行ずつ因果の向きと大きさが格納されている *数法則発見法(RF5法)によるモデル化 [#tdd1a952] 数法則発見法の1つであるRF5を用いて経済変数間のモデル化を行う. RF5のプログラムは以下に貼っておく. **AGIP(適応型プルーニング)の実行 [#agip] AGIP(Adaptive Gradient-based Importance Pruning)では、学習の進行度合い(Loss)に応じて動的にプルーニングの削除率を調整する手法を適用する。 RF5では何度も反復を行い学習することでモデルを作成するため,反復回数を設定する必要がある. 反復回数を設定する際にはwhile s < 500:の500の部分を反復したい回数に設定する. この反復回数は従来研究では10000回や20000回が望ましいと述べられていたのでそれ以上の値に設定した方が良いだろう. 目的: 学習状態に応じた安全なプルーニングを実現し、モデルの崩壊(急激な精度低下)を防ぎながらパラメータを削減する。 実行して得られたBICが低ければ低いほど良い結果をとりやすいのでBICが最も小さくなったものの結果を見て考察を行えばよい. 特徴: 単純な重みの絶対値ではなく、「重みと勾配の積の絶対値」を重要度スコアとして算出し、層(Layer)ごとに下位パラメータを削除する。 さらに、Lossが大きい時(誤差が大きい時)にはプルーニングの進行を遅らせる安全装置(Safety Mechanism)が組み込まれており、モデルの容量を温存しようとする適応的な挙動を示す。 RFを実行する際はDriveのRF_real_experiment100.pyを実行すればよい. 実行する際は実際に用いるデータを置き換える必要がある. その際には169行目のpd.read_csv('データのパス(csv形式)')に置き換える. データの形式の例を以下に示す. #ref(data.png.jpg) データの内容としては右端の列に目的変数,それ以外の列に説明変数のデータを入れる. 結果はtheta.csvに出力され,それをRF5の定式化に当てはめることで構築されたモデルを復元することができる. 以下にtheta.csvの中身を示す. #ref(theta.jpg) RF5の定式化は以下に示す. #ref(RF_teishikika.png) θは(w_0,w_j,w_jk)かなっている. このθの中身がtheta.csvには格納され,これを用いて復元を行う. *因果3Dグラフの作成 [#s53cb794] VAR-LiNGAMによって得られた結果から因果3Dグラフを作成する. ここではVARLiNGAMで得られた合計因果関係を格納したcsvファイルを用いて3Dグラフを作成する. csvファイルは以下の形式で保存されており,以下のプログラムでグラフが作成される. import pandas as pd import json from pyvis.network import Network def kyoki_word_network(): got_net = Network(height="1000px", width="95%", bgcolor="#FFFFFF", font_color="black", notebook=True, directed=True) got_data = pd.read_csv("ultra_kekka.csv")[:2000] sources = got_data['from'] targets = got_data['to'] weights = got_data['effect'] unique_nodes = set(sources) | set(targets) nodes = [{"id": node, "label": node, "title": node} for node in unique_nodes] edges = [{"from": src, "to": dst, "color": 'red' if w < 0 else 'blue', "label": str(round(w, 4)), "arrows": "to"} for src, dst, w in zip(sources, targets, weights)] nodes_json = json.dumps(nodes, ensure_ascii=False) edges_json = json.dumps(edges, ensure_ascii=False) html_code = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Your Network</title> <style> #mynetwork { height: 1000px; width: 95%; border: 1px solid lightgray; } </style> </head> <body> <div id="mynetwork"></div> <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script> <script> var container = document.getElementById("mynetwork"); var nodes = new vis.DataSet(""" + nodes_json + """); var edges = new vis.DataSet(""" + edges_json + """); var data = {nodes: nodes, edges: edges}; var options = {}; var network = new vis.Network(container, data, options); // ノードのダブルクリック用のイベントリスナーを追加 network.on("doubleClick", function (params) { if (params.nodes.length > 0) { var nodeId = params.nodes[0]; var nodeLabel = nodes.get(nodeId).label; // ページ遷移や他のダブルクリックされたノードに基づくアクションを実行 window.location.href = "page/" + nodeLabel; // ページ遷移のURLを変更してください } }); </script> </body> </html> """ with open("your_network.html", "w", encoding="utf-8") as html_file: html_file.write(html_code) got_net.show_buttons(filter_=['physics']) return got_net kyoki_word_network() jscode=""の部分でクリックアクションなどを設定している. 今回はダブルクリックしたときにページ遷移するようにした. #ref(networka.PNG) ここからノードをクリックすることで,ページ遷移を行う. **3Dグラフによる可視化 [#u182dd73] 改善版としてグラフをよりアトラクティブにするシステムを作成した. 改善前ではノードを引っ張ることしかできていなかったが,クリックしドラッグを行うことで様々な角度からネットワークを確認できるようにした. 実行手順: AGIP実行用のコードを起動する。学習プロセスにおいて、指定した目標スパース率(例:33%)に向けて段階的にマスク処理が行われる過程を確認する。 その際にはthree.jsによる可視化を行い,アプリケーションの作成を行った. **AGIP+TA(提案手法)の実行 [#agip_ta] 本研究のコアとなる提案手法であり、上記のAGIPに「ターミナルアトラクタ(TA)項」を組み合わせたハイブリッド手法である。 three.jsについては以下のサイトを参考にした. https://github.com/vasturiano/3d-force-graph 以下に改善版のプログラムを示す. const highlightLinks = new Set(); const highlightNodes = new Set(); let hoverNode = null; 目的: AGIPによるパラメータ削減の安全性に加え、TA項による「有限時間収束性」を付与し、プルーニングによるネットワークの構造破壊から素早くかつ高精度に回復(再学習)させる。 const Graph = ForceGraph3D() (document.getElementById("three")) .jsonUrl('http://127.0.0.1:5000/static/output2.json') .nodeColor('#ffffff') .nodeLabel('id') .nodeRelSize(20) .nodeThreeObject(node => { const sprite = new SpriteText(node.id); sprite.material.depthWrite = false; // make sprite background transparent sprite.color = node.color; sprite.textHeight = 7.5; return sprite; }) .linkThreeObject(link => { // extend link with text sprite const sprite = new SpriteText(`${link.value}`); sprite.color = 'blue'; sprite.textHeight = 1.5; return sprite; }) .linkPositionUpdate((sprite, { start, end }) => { const middlePos = Object.assign(...['x', 'y', 'z'].map(c => ({ [c]: start[c] + (end[c] - start[c]) / 2 // calc middle point }))); 特徴: 重みの更新式に、TA項(分子に誤差のβ乗、分母に勾配ノルムの2乗)を導入している。 これにより、勾配が消失しやすい平坦な領域(プラトー)に陥っても、地形(勾配)の影響をキャンセルし、誤差に基づく駆動力で強制的に収束へ向かわせることができる。 なお、TA項の反発によって一時的にLossが上昇した場合、AGIPの安全装置が強く働き、最終的なスパース率が自動的に低く(例:26%などに)抑えられるという相互作用が発生する。 // Position sprite Object.assign(sprite.position, middlePos); }) .onNodeClick(node => { var baseUrl = "page/" + node.id; window.location.href = baseUrl; }) 実行手順: ノートブック「TA+AGIP30%_maxgrad4.0.ipynb」を実行する。 定義された「AGIP_TA_Trainer」クラスを通じて、TA項を組み込んだオプティマイザによる学習とAGIPによるプルーニングが同時に実行される。最終結果として出力されるLossグラフ、PPL、および自動調整された最終スパース率を記録し、ベースラインの手法と比較・考察を行う。 .onNodeHover(node => { // ノードにホバーした時の処理 hoverNode = node ? node.id : null; updateHoverText(); // テキストを更新する関数を呼び出す }) .linkOpacity(0.25) .linkDirectionalArrowLength(3) .linkDirectionalArrowRelPos(1) .linkCurvature(0.01) .linkDirectionalParticleWidth(2) .linkDirectionalParticles("value") .linkDirectionalParticleSpeed(d => d.value * 0.01) .linkThreeObjectExtend(true) .linkColor(link=>{ if (link.value > 0){ return 'red' } else { return 'blue' } }) .linkWidth(1) .backgroundColor("#f8f8f8"); function updateHoverText() { // ホバー時のノードのテキストサイズを変更 Graph.nodeThreeObject(node => { const sprite = new SpriteText(node.id); sprite.material.depthWrite = false; sprite.color = node.color; sprite.textHeight = hoverNode === node.id ? 15 : 7.5; // ホバー時にテキストサイズを変更 return sprite; }); } これを実行する際は改善前と同じくflaskフォルダ内のapp.pyを実行すればよい. 以下に実行結果を示す. #ref(2.png)