Open7

Flask + Stable DiffusionでWebアプリ開発のメモ

でじぃでじぃ

Windowsでやろうとするも、pyenv自体はWindowsに非対応だし微妙なので、WSL2でとりあえずやってみる。完全にメモ用ですすみません
Streamlitでは非同期処理ができないらしく、時間の計測ができないので諦める(調査不足だった)
代わりにFlaskを使うことにする

使う(予定の)もの

  • WSL2
  • Python
    • Poetry
    • pyenv
    • Streamli
      • streamlit-drawable-canvas
    • Flask
      • bootstrap-flask

環境構築

コマンドを色々実行していく。PowerShellはWindows側のシェルで、BashはUbuntuのデフォルトシェル。

WSL2

PowerShell
wsl --install

で普通に入れる。仮想化の有効化とかはググれば出てくるので略。

PowerShell
wsl --update

もしておいたほうがいいかも。
今回はUbuntuを使う

Ubuntu

Ubuntuを最初に起動すると初期設定のウィザードが始まるので、指示に従ってusernameやパスワードなどを設定する。パスワードは安全な場所にメモっておく。
以下はほとんどこの記事のままなので、元記事見たほうがいいかも…(ありがとうございます)
https://zenn.dev/claustra01/articles/0d8efd08905526

Ubuntuのライブラリのアップデート

Bash
sudo apt update
Bash
sudo apt upgrade

でUbuntuのライブラリ類をアップデートする。

pyenvを入れる

pyenvの依存関係を入れる

Bash
sudo apt install libssl-dev libffi-dev libncurses5-dev zlib1g zlib1g-dev libreadline-dev libbz2-dev libsqlite3-dev make gcc

pyenv本体を入れる

Bash
curl https://pyenv.run | bash

環境変数を設定

Bash
echo '' >> ~/.bashrc
Bash
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
Bash
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
Bash
echo 'eval "$(pyenv init --path)"' >> ~/.bashrc

この4つのコマンドで環境変数を設定する(らしい)。

Ubuntuからログアウトする

Bash
exit

とかのコマンドを実行して一旦Ubuntuからログアウトする。再起動が必須かは不明だが、自分はログインし直しでOKだった。一旦ログアウトしないと、入れたpyenvが使えないので注意する。

Ubuntuにログインしてインストールを確認

Bash
pyenv --version

を実行してエラーが出ずに実行できたらOK。pyenvのバージョンが表示されるはず。

使いたいバージョンのPythonをインストール

Bash
pyenv install <Pythonのバージョン番号>

で使いたいバージョンのPythonをインストールする。今回は3.10.6を使う(Stable Diffusionの関係上)。インストール可能なバージョンの一覧は

Bash
pyenv install --list

で表示できる。

ディレクトリを移動

使いたいバージョンのPythonをインストールしたら、今度は適当なディレクトリまでcd <移動先ディレクトリのアドレス>コマンドで移動する。適切なディレクトリが無ければ、mkdir <ディレクトリ名>で開発用のディレクトリを作ってそこに移動する。

ディレクトリにPythonのバージョンを設定

Bash
pyenv local <Pythonのバージョン番号>

で、そのディレクトリにPythonのバージョンを設定する。今回は3.10.6。設定できたら、そのディレクトリに.python-versionというファイルが生成されているはずなので、一応ls -allコマンドで表示させてみる(隠しファイルなので-allが無いと表示できない)。

Poetry

Poetryのインストール

公式のドキュメント
https://python-poetry.org/docs/#installing-with-the-official-installer

Bash
curl -sSL https://install.python-poetry.org | python3 -

を実行してPoetry(Pythonのパッケージマネージャ)を入れる。

fish(その他のシェルでも)でパスを通す方法

curl -sSL https://install.python-poetry.org | python3 - を実行した際に表示されるexport PATH="$HOME/.poetry/bin:$PATH"みたいなものを、/.config/fish/config.fishに書き込む(config.fishは設定ファイル)。
本当はfishにはfish_add_pathとかsetとかがあるらしいが、よく分かっていないので、とりあえずBash互換(?)のこれでしのいだ

Ubuntuに入りなおす

これもUbuntuにログインし直す(再起動?)が必要なので、やる

インストール確認

Bash
poetry --version

を実行して、正常に実行されるか確認する。正常ならOK。

簡単な使い方集

Poetry

プロジェクトを作る

Poetryのプロジェクトを作成したいディレクトリに移動したら、そこで

Bash
poetry init --python ^3.10.6

を実行する。これは、Pythonの3.10.6を使ってPoetryプロジェクトを作成するというコマンド。
poetry initコマンドを実行するとPoetryの対話型の環境構築ウィザードが始まる。

  • Package Nameは今回は~~streamlit-ai~~flask-aiにした。入力してEnter。
  • Versionも適当でいいのでそのままEnter。
  • Discriptionは無くていいので空欄のままEnter。
  • Autherはまだこの段階では決めないのでnとだけ入力してEnter。
  • Licenseも未定なのでそのままEnter。
  • その後も色々聞かれるが、デフォルトの回答であるyesで多分いいと思うのでそのままEnter。
  • 最後の生成しますか?ももちろんEnter。
    これが終われば、そのディレクトリにPoetryのプロジェクトが作成完了しています。

パッケージの追加

Bash
poetry add <Pythonのパッケージ名>

で、その環境にPythonのパッケージを追加する。
今回は、とりあえずflaskbootstrap-flaskを入れるので、

Bash
poetry add flask
Bash
poetry add bootstrap-flask

この2つを(poetry init済みのディレクトリで)実行する。

使うのをやめたStreamlitの部分

今回は、とりあえずstreamlitstreamlit-drawable-canvasを入れるので、

Bash
poetry add streamlit
Bash
poetry add streamlit-drawable-canvas

この2つを(poetry init済みのディレクトリで)実行する。

Pythonファイルの実行

Pythonファイルを実行するには、以下のコマンドを実行する。

Bash
poetry run python <実行するファイルの名前>

例えば、今回使うFlaskで、公式ドキュメントに載っているようにhello.pyに記述したプログラムを実行する場合は以下。

Bash
poetry run flask --app hello run

このコマンドは、通常はflask --app hello runだけで実行できるプログラムを、Poetryの仮想環境を使わずに実行している。

Poetryの仮想環境を使う場合

Bash
poetry shell
Bash
flask --app hello run

これでOK。仮想環境から出るには

Bash
exit

を実行する。exitを忘れなければ仮想環境に入ってから実行するほうがいいかも?

使うのをやめたStreamlitの部分2

例えば、今回使うStreamlitのデフォルトプロジェクトの場合は以下。

Bash
poetry run python -m streamlit hello

このコマンドは、通常はpython -m streamlit helloだけで実行できるプログラムを、Poetryの仮想環境を使わずに実行している。

Poetryの仮想環境を使う場合

Bash
poetry shell
Bash
python -m streamlit hello

これでOK。仮想環境から出るには

Bash
exit

を実行する。

でじぃでじぃ
使わないことにしたStreamlitの部分

Poetryの仮想環境を使う場合、

Bash
python -m streamlit hello

ではなく、

Bash
streamlit run hello

でも良いっぽい

でじぃでじぃ

Flask

Flaskのインストール

poetry init済みのディレクトリで、以下のコマンドを実行するだけ

Bash
poetry add flask
一回入れたものの、いらない気がする工程

今回はWebのフロントエンド実装のために、CSSフレームワークとしてBootstrapを使うので、bootstrap-flaskも入れる

Bash
poetry add bootstrap-flask

Bootstrap5の導入

FlaskだけではWebの表示部分を制御することができないので、表示部分はHTML+CSS+JavaScriptで実装する。そこで、CSSフレームワークにBootstrap5を使う。

ディレクトリ移動

Bootstrapのファイルをダウンロードして展開するので、自分の都合の良いディレクトリに移動しておく。
ちなみに、配置は以下の通りにします。

Bash
 flask-ai
│   ├── flaskr
│   │   ├── app.py
│   │   ├── saved_canvas
│   │   ├── static
│   │   │   ├── css
│   │   │   │   ├── bootstrap.css
│   │   │   │   ├── bootstrap.css.map
│   │   │   │   ├──          ︙
│   │   │   │   └── style.css
│   │   │   └── js
│   │   │       ├── bootstrap.bundle.js
│   │   │       ├──          ︙

wgetしてダウンロード

Bootstrapのダウンロードページにダウンロードのリンクがあるので、それをコピーして、Ubuntuのターミナルで以下を実行する。

Bash
wget <コピーしたURL(末尾は.zipのはず)>

執筆時点では以下の通り。

Bash
wget https://github.com/twbs/bootstrap/releases/download/v5.3.0/bootstrap-5.3.0-dist.zip

Zipの解凍(展開)のためにunarコマンドを使えるように

Zipファイルを解凍するコマンドは入ってないので、解凍のためにunarコマンドを使えるようにする。
なんかunzipとかよりunarのほうがいいらしいので。[1]

Bash
sudo apt install unar

これをUbuntuのターミナルで実行すればOK。

Zipを解凍

以下のコマンドでZipを解凍する。

Bash
unar <ダウンロードしたZipファイルのファイル名>

(必要なら)ファイルの移動

先に示したようなディレクトリ構造になるよう、ファイルを移動させる。ファイル・ディレクトリの移動はmvコマンドを使うが、ディレクトリ名の変更にもmvコマンドを使う[2]

ディレクトリ名の変更

Bash
mv <今のディレクトリ名> <変更後のディレクトリ名>
mv templetes/ templates/

ファイル名の変更

ファイル名を変更したい場合も、同様の要領でできる。

Bash
mv <今のファイル名> <変更後のファイル名>
mv tekitou.png tekitou2.png

Flaskのディレクトリ構造

ここは正直大規模化したときに破綻しそうなので怖いけど、とりあえずGPT-4くんが提示したディレクトリ構造にしてみる(動きはする)
ディレクトリ構造が以下。flask-aipoetry initしたディレクトリ。

Bash
 flask-ai
│   ├── flaskr
│   │   ├── app.py
│   │   ├── saved_canvas
│   │   ├── static
│   │   │   ├── css
│   │   │   │   ├── bootstrap-grid.css
│   │   │   │   ├── bootstrap-grid.css.map
│   │   │   │   ├── bootstrap-grid.min.css
│   │   │   │   ├── bootstrap-grid.min.css.map
│   │   │   │   ├── bootstrap-grid.rtl.css
│   │   │   │   ├── bootstrap-grid.rtl.css.map
│   │   │   │   ├── bootstrap-grid.rtl.min.css
│   │   │   │   ├── bootstrap-grid.rtl.min.css.map
│   │   │   │   ├── bootstrap-reboot.css
│   │   │   │   ├── bootstrap-reboot.css.map
│   │   │   │   ├── bootstrap-reboot.min.css
│   │   │   │   ├── bootstrap-reboot.min.css.map
│   │   │   │   ├── bootstrap-reboot.rtl.css
│   │   │   │   ├── bootstrap-reboot.rtl.css.map
│   │   │   │   ├── bootstrap-reboot.rtl.min.css
│   │   │   │   ├── bootstrap-reboot.rtl.min.css.map
│   │   │   │   ├── bootstrap-utilities.css
│   │   │   │   ├── bootstrap-utilities.css.map
│   │   │   │   ├── bootstrap-utilities.min.css
│   │   │   │   ├── bootstrap-utilities.min.css.map
│   │   │   │   ├── bootstrap-utilities.rtl.css
│   │   │   │   ├── bootstrap-utilities.rtl.css.map
│   │   │   │   ├── bootstrap-utilities.rtl.min.css
│   │   │   │   ├── bootstrap-utilities.rtl.min.css.map
│   │   │   │   ├── bootstrap.css
│   │   │   │   ├── bootstrap.css.map
│   │   │   │   ├── bootstrap.min.css
│   │   │   │   ├── bootstrap.min.css.map
│   │   │   │   ├── bootstrap.rtl.css
│   │   │   │   ├── bootstrap.rtl.css.map
│   │   │   │   ├── bootstrap.rtl.min.css
│   │   │   │   ├── bootstrap.rtl.min.css.map
│   │   │   │   └── style.css
│   │   │   └── js
│   │   │       ├── bootstrap.bundle.js
│   │   │       ├── bootstrap.bundle.js.map
│   │   │       ├── bootstrap.bundle.min.js
│   │   │       ├── bootstrap.bundle.min.js.map
│   │   │       ├── bootstrap.esm.js
│   │   │       ├── bootstrap.esm.js.map
│   │   │       ├── bootstrap.esm.min.js
│   │   │       ├── bootstrap.esm.min.js.map
│   │   │       ├── bootstrap.js
│   │   │       ├── bootstrap.js.map
│   │   │       ├── bootstrap.min.js
│   │   │       ├── bootstrap.min.js.map
│   │   │       ├── countdown.js
│   │   │       └── draw.js
│   │   └── templates
│   │       └── index.html
│   ├── poetry.lock
│   └── pyproject.toml

デフォルトではstaticjs,templetesなどのディレクトリは無いので、自分で作ってそこにさらに自分でHTMLやらJavaScriptのファイルを作成していくことになるのですが、ディレクトリ名を間違えるとエラーで動かなくなるので注意が必要です。特に、templetesはスペルミスしやすい(templatesにしてて2時間溶かした顔)。
__pychache__はアプリを動かすとその時に自動で作られるので気にしなくてOKです。

treeコマンドの導入

デフォルトではtreeコマンド[3]が使えないと思うので、これも入れる。

Bash
sudo apt install tree

以下のようにすると、カレントディレクトリ以下のディレクトリ構造が表示される。

Bash
tree .

サンプルを動かす

GPT-4に聞いて吐かせたサンプルプログラムを以下に示します。コピペすれば動きはするはず。

flask-ai/flaskr/app.py
from flask import Flask, render_template, request
import os
import base64
import re
from datetime import datetime

template_dir = os.path.abspath('./templates')
app = Flask(__name__, template_folder=template_dir)

@app.route("/")
def home():
    return render_template('index.html')

@app.route('/saved_canvas', methods=['POST'])
def save_canvas():
    img_data = request.form['img'] # POSTリクエストから画像データを取得
    img_data = re.sub('^data:image/.+;base64,', '', img_data) # data URLのヘッダ部分を削除

    # 現在の時刻からファイル名を生成
    now = datetime.now()
    filename = now.strftime("%Y-%m-%d-%H%M-%S") + ".png"

    # 保存先ディレクトリを指定
    save_dir = '自分の環境に合わせて変える、~/dev/python/quickdiffusion/files/saved_canvasとか'
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)  # ディレクトリが存在しない場合は作成

    with open(save_dir + filename, "wb") as fh:
        fh.write(base64.b64decode(img_data)) # 画像データをデコードしてファイルに書き込む

    return "", 200 # 正常終了のステータスコードを返す

if __name__ == '__main__':
    app.run(debug=True)
flask-ai/flaskr/static/js/countdown.js
document.getElementById("startButton").addEventListener("click", function() {
    var timeleft = 10;
    var countdownTimer = setInterval(function(){
        if(timeleft <= 0){
            clearInterval(countdownTimer);
            document.getElementById("message").innerHTML = "Finished";
            // カウントダウンが0になったらキャンバスの内容をサーバーに送信
            sendCanvasData();
        } else {
            document.getElementById("message").innerHTML = timeleft + " seconds remaining";
        }
        timeleft -= 1;
    }, 1000);

    // Canvasをクリア
    canvas.clear();
});

// 画像を表示する機能
document.getElementById('myImage').src = "path/to/your/image.png";

// キャンバスのデータをサーバーに送信する関数
function sendCanvasData() {
    var dataURL = canvas.toDataURL(); // canvasの内容をPNGのdata URL形式で取得
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/save_canvas", true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.send("img=" + encodeURIComponent(dataURL)); // data URLをPOSTリクエストのボディに含めて送信
}
flask-ai/flaskr/static/js/draw.js
// Javascriptファイル: draw.js
var canvas = new fabric.Canvas('myCanvas', {
    backgroundColor: 'rgb(255,255,255)' // 背景を白に設定
});
var isDrawing = false;

canvas.isDrawingMode = false; // 初期状態では描画できないようにする
canvas.freeDrawingBrush.width = 5; // 線の太さを5に設定
canvas.freeDrawingBrush.color = "black"; // 線の色を黒に設定

canvas.on('mouse:down', function(o){
    isDrawing = true;
});

canvas.on('mouse:up', function(o){
    isDrawing = false;
});

// スタートボタンが押されたら描画可能にする
document.getElementById("startButton").addEventListener("click", function() {
    canvas.isDrawingMode = true;
});

flask-ai/flaskr/templetes/index.html
<!doctype html>
<html lang="ja">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">

    <!-- Custom CSS -->
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">

    <!-- Include Fabric.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>

    <title>My App</title>
  </head>
  <body>
    <div class="container">
        <div class="row">
            <div class="col text-center">
                <h1 id="message">Click the button to start countdown</h1>
            </div>
        </div>
        <div class="row">
            <div class="col">
                <canvas id="myCanvas" width="400" height="250" style="border:1px solid #d3d3d3;">
                </canvas>
            </div>
            <div class="col">
                <img id="myImage" src="#" />
            </div>
        </div>
        <div class="row">
            <div class="col text-center">
                <button id="startButton" class="btn btn-primary">Start</button>
            </div>
        </div>
    </div>

    <!-- jQuery and Bootstrap Bundle (includes Popper) -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
    <script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>

    <!-- Custom JavaScript -->
    <script src="{{ url_for('static', filename='js/countdown.js') }}"></script>
    <script src="{{ url_for('static', filename='js/draw.js') }}"></script>
  </body>
</html>
flask-ai/flaskr/static/css/style.css
body {
    background-color: #f8f9fa;
}

#myCanvas {
    background-color: #ffffff;
    width: 200%;
    height: 750px;
}

#myImage {
    width: 100%;
    height: auto;
}

#startButton {
    margin-top: 20px;
}

h1 {
    margin: 50px 0;
}
脚注
  1. https://qiita.com/supersaiakujin/items/c6b54e9add21d375161f ↩︎

  2. https://uxmilk.jp/8334 ↩︎

  3. https://atmarkit.itmedia.co.jp/ait/articles/1905/10/news017.html ↩︎

でじぃでじぃ

コマンドラインから画像を開く

カウントダウンが0になると画像が保存されるようにしているが、実際きちんと保存されているかは確認しないと分からない。それを確認するときに、コマンドラインから画像を開きたいと思うので、eogコマンドを使えるようにする(eogはEye of GNOME Image Viewerの頭文字)

Bash
sudo apt install eog

eogの使い方

Bash
eog <ファイル名>
eog 2023-01-29.png

これだけ

でじぃでじぃ

WSL2とVisual Studio Codeで快適に開発する

WSL2にUbuntuを入れ、テキストエディタにVSCodeを使っている場合は、拡張機能をインストールすることで快適に使えるようになります。
詳細は以下の記事を参考にしてください。記事のDocker以下の部分は関係ないです
https://zenn.dev/s_ryuuki/articles/4b9631674adea4

Git/GitHubの使い方

まずはこのあたりを読むと分かりやすいと思います。要点がしっかりまとまっていて参考になります。
https://tech-blog.rakus.co.jp/entry/20200529/git

Gitのインストール

Gitもコマンドで入れます。WSL2を使っていても、WindowsにGitを入れる必要はありません。

Bash
sudo apt install git-all

以下のコマンドを実行して、バージョンが表示されればOKです。

Bash
git --version

Gitの初期設定

Gitのユーザー名とメールアドレスを設定します。ユーザー名はなんでもいいらしいですが、分かりやすいのでGitHubのユーザー名にしておくのがいいと思います。

Bash
git config --global user.name <設定するユーザー名>
git config --global user.name digitalsp

メールアドレスは、GitHubの登録に使ったメールアドレスにします。GitHubでメールアドレスを非公開にしている場合(SettingsEmailsのKeep my email addresses privateチェックボックスに☑している場合)、そのままのメールアドレスが使えないので、GitHubアカウントの設定(SettingsEmails)で、設定するメールアドレスをコピーしてきます。
<数字>+<ユーザー名>@users.noreply.github.comみたいなのがそれです。

Bash
git config --global user.email <設定するメールアドレス>
git config --global user.email xxxxxxxx+digitalsp@users.noreply.github.com

設定できたら、確認のために以下を実行してください。user.email=<設定したメールアドレス>user.name=<設定したユーザー名>が表示され、設定されていればOKです。

Bash
git config --list | grep user

GitHub CLIのインストール

GitHubをシェルから使えるようにするGitHub CLIをインストールします。シェルで以下のコマンドを実行します。

Bash
sudo apt update
sudo apt install gh

多分これで入ると思いますが、入らない場合は以下を実行してください

Bash
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update \
&& sudo apt install gh -y
Bash
sudo apt update
sudo apt install gh

以下を実行し、バージョン名が表示されればOKです

Bash
gh --version

GitHubにログイン

GitHub CLIを使ってGitHubにログインします。まずは、以下のコマンドを実行します。

Bash
gh auth login

表示される指示に従えばログインできるはずです。指示は英語なので翻訳してください。

GitHub上のリポジトリをクローン

GitHubリポジトリをローカルにクローン(≒ダウンロード)します。

Bash
gh repo clone <リポジトリのオーナー>/<リポジトリ名>
# octocat/Hello-Worldというリポジトリをクローンする場合
gh repo clone octocat/Hello-World

ブランチを分ける

ブランチを分けます。このコマンドを実行しないと、mainブランチに変更が記録されてしまうので、必ず実行しましょう。

Bash
git checkout -b <新しいブランチ名>

Gitに記録させるファイルを追加

以下のコマンドを実行して、Gitに変更履歴を記録させるファイルを追加します。

Bash
git add <ファイル/ディレクトリの指定>
# そのディレクトリ以下の全てのファイル/ディレクトリを追加する場合
git add .
# 任意のファイル(ここでは`sample.txt`)を追加する場合
git add sample.txt

Gitに記録させたくないファイルがあり、それが多い場合は、.gitignoreというファイルで追加しないファイルを設定します。今回は、私が一通り設定した.gitignoreをGitHubに上げていて、GitHubからリポジトリをクローンしたら、この.gitignoreファイルも同時にダウンロードされるので、特に設定は不要です。今回開発に使うのは非公開リポジトリですし、今回は問題になることは無いかと思いますが、GitHubのリポジトリがごちゃごちゃするので、プログラムの動作に関係のないファイルや、アクセストークンなどの機密情報が書かれたファイルなどはgit addしないほうがよいです。

変更をCommitして記録

変更したファイルの変更を記録します。このコマンドは、開発が一段落したとき、例えば機能追加が終わったときなどに実行します。大量の変更を1度のCommitに含めると可読性が下がるので、適当な変更量に留めるのがよいです。適当な変更量の目安としては、1つのコミットの変更内容が、下記記事にあるコミット種別1つに収まる程度です。つまり、改善と機能追加を1つにまとめるのは多すぎ(1つに詰め込みすぎ)ということになります。

Commitには、変更がどのようなものであるかをコミットメッセージとして記述します。コミットメッセージの書き方については、だいたい以下の記事に従ってもらえればOKです(厳密に従う必要は無いです)。
https://qiita.com/itosho/items/9565c6ad2ffc24c09364
上記記事の、通常版のコミット種別を採用します。

…偉そうに書いていますが自分も初めてみたいなものなので、そんなに気にしすぎないでください…!

コマンド

コマンドは以下の通りです。git commitを行うと、テキストエディタが起動すると思うので、そこに上述のコミット種別などのコミットメッセージを書いていきます。書き終わって保存できたら、ファイルを閉じるとコマンドが実行できます。

Bash
git commit

リモートリポジトリ(GitHubのリポジトリ)にPush

Commitした変更を、リモートリポジトリ(今回はGitHub上のリポジトリ)に反映します。これをPushといいます。

Bash
git push origin <(さっき作った)ブランチ名>

Pull Requestの作成

Pull Requestを作成します。Pull Requestとは、あるブランチに他のブランチを合わせてほしいと要求することです。今回は、主となるmainブランチに、他のブランチ(自分で作ったブランチ)を統合するように要求することになります。

Bash
gh pr create --base main --head <(さっき作った)ブランチ名>

これで、Pull Requestを作成できます。もしくは、(説明はしませんが)Push後に、GitHubのWebを開いて、今回のリポジトリのページから、GUIでPull Requestを作成することも可能です。

Mergeを待つ

PR(Pull Requestの略)を送ったら、管理者である私がPRを承認して、ブランチをMerge(マージ、統合という意味)します。ここで管理者以外ができることはないので、しばしお待ち下さい。

Merge後

PRがマージされたら、自分のPCのプログラムを更新しなければなりません。そこで、以下のコマンドを実行します。

Bash
git checkout main
git pull origin main

これで、自分のローカル環境にも、マージされたPRが反映されます。

管理者的マージメモ

PRが来たときは以下のようにする。
https://blog.qnyp.com/2013/05/28/pull-request-for-github-beginners/

Bash
git fetch

でローカルにPRなどのブランチを反映

Bash
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 2), reused 6 (delta 2), pack-reused 0
Unpacking objects: 100% (6/6), 1.42 KiB | 483.00 KiB/s, done.
From https://github.com/digitalsp/{リポジトリ名}
 * [new branch]      add_firstpage -> origin/add_firstpage*

こんな感じになればfetchはOK。

Bash
git branch -a

ブランチ一覧を表示。

Bash
* main
  remotes/origin/HEAD -> origin/main
  remotes/origin/add_firstpage
  remotes/origin/main

PRが来てるブランチのブランチ名を確認する。

Bash
git checkout -b add_firstpage origin/add_firstpage
git checkout -b <ローカルリポジトリでの表示ブランチ名> <origin/リモートブランチ名>

現在のブランチをPRが来ているブランチに切り替える。
https://qiita.com/miriwo/items/53b319d3d3a3759b30a7

Bash
M       pyproject.toml
Branch 'add_firstpage' set up to track remote branch 'add_firstpage' from 'origin'.
Switched to a new branch 'add_firstpage'

こんな感じならOK。これでローカルで動作確認ができるようになったので、動作確認なんかをして、あとはGitHubのWebからマージする。

でじぃでじぃ

Intel OneAPI Toolkitを入れる

自分の手持ちPCのGPUはIntelなので、めんどくさいがUbuntuにIntel OneAPI Toolkitを入れる(パッケージ名はintel-basekit)

インストール

intel-basekitのインストール

Bash
wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \ | gpg --dearmor | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null
Bash
echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list
Bash
sudo apt update

これでパッケージの取得先を設定

Bash
sudo apt list intel-basekit -a

これでインストール可能なパッケージの一覧を表示
今回実行したところ、以下の出力があった

Bash
sudo apt list intel-basekit -a
Listing... Done
intel-basekit/all 2024.0.1-43 amd64
intel-basekit/all 2024.0.0-49522 amd64
intel-basekit/all 2023.2.0-49384 amd64
intel-basekit/all 2023.1.0-46401 amd64
intel-basekit/all 2023.0.0-25537 amd64
intel-basekit/all 2022.3.1-17310 amd64
intel-basekit/all 2022.3.0-8767 amd64
intel-basekit/all 2022.2.0-262 amd64
intel-basekit/all 2022.1.2-146 amd64
intel-basekit/all 2022.1.1-119 amd64
intel-basekit/all 2021.4.0-3422 amd64
intel-basekit/all 2021.3.0-3219 amd64
intel-basekit/all 2021.2.0-2883 amd64
intel-basekit/all 2021.1.0-2659 amd64

自分はvladmandic/automaticで既にOneAPI経由でStable Diffusionを使っていて、このライブラリは最新版が2024年1月(?)のものを使っているので、多分2024.0.0-49522だろうと推測して入れることにした(実は2024.0.0っぽいのはあとの工程をやっているときに気づいたが、なんか自動で2024.0.0が入ったり入らなかったりしていたので気にしないことにした。多分最初からやるなら2024.0.0のほうがいいと思う)。具体的なコマンドは以下の通り。ちなみに全部で14GBある超巨大ファイル群なので、空き容量に注意する。

Bash
sudo apt-get install intel-basekit=2024.0.0-49522

Pythonのパッケージのインストール

https://ugo-robot.hatenablog.com/entry/2023/06/13/121702
このページと、先述のvladmandic/automaticインストールスクリプト部分を参考に、poetry installでPythonのパッケージを入れようとしたところ、パッケージの参照先URLが違うことに気づき、調べてみると、PyPlに無いパッケージを入れる方法を使うらしい。
https://zenn.dev/zerebom/articles/b338784c8ac76a
記事の内容に従って、URLを設定して入れてみる。

インストールスクリプト部分の抜粋(457行目~)
install.py
        if "linux" in sys.platform:
            torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.1.0a0 torchvision==0.16.0a0 intel-extension-for-pytorch==2.1.10+xpu --extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/')
            os.environ.setdefault('TENSORFLOW_PACKAGE', 'tensorflow==2.14.0 intel-extension-for-tensorflow[xpu]==2.14.0.1')
Bash
poetry source add torch_ipex --priority=explicit https://pytorch-extension.intel.com/release-whl/stable/xpu/us/

これで、優先度Explicit(最高?)でURLが追加される。あとは、ダウンロード元をさっき指定したやつにしてインストールするだけ。ちなみにファイルサイズがクソデカいらしく、コマンドの実行に15分くらいかかるほか、ストレージの容量も10GB単位で減るので注意。

Bash
poetry add torch==2.1.0a0 torchvision==0.16.0a0 intel-extension-for-pytorch==2.1.10+xpu --source torch_ipex

これで指定バージョンが入るはず。Intel公式の方法とかだとpipを使っているが、今回使っているのはpipではなくPoetryなので、こういうダウンロード元を指定する操作が必要。

Bash
Updating dependencies
Resolving dependencies... (879.8s)

Package operations: 21 installs, 0 updates, 0 removals

  • Installing mpmath (1.3.0)
  • Installing typing-extensions (4.9.0)
  • Installing annotated-types (0.6.0)
  • Installing certifi (2024.2.2)
  • Installing charset-normalizer (3.3.2)
  • Installing filelock (3.13.1)
  • Installing fsspec (2024.2.0)
  • Installing idna (3.6)
  • Installing networkx (3.2.1)
  • Installing pydantic-core (2.16.2)
  • Installing sympy (1.12)
  • Installing urllib3 (2.2.1)
  • Installing numpy (1.26.4)
  • Installing packaging (23.2)
  • Installing pillow (10.2.0)
  • Installing psutil (5.9.8)
  • Installing pydantic (2.6.1)
  • Installing requests (2.31.0)
  • Installing torch (2.1.0a0+cxx11.abi)
  • Installing intel-extension-for-pytorch (2.1.10+xpu)
  • Installing torchvision (0.16.0a0+cxx11.abi)

Writing lock file

こういうのが出るので、実行が終わればOK。

テスト用スクリプトを実行すると、ImportError: libmkl_gnu_thread.so.2: cannot open shared object file: No such file or directoryというエラーが出ていた。

インストール確認用テストスクリプト
test-script.py
import os
import os
os.system("source /opt/intel/oneapi/setvars.sh")
import torch
import intel_extension_for_pytorch as ipex
print(torch.__version__)
print(ipex.__version__)
[print(f'[{i}]: {torch.xpu.get_device_properties(i)}') for i in range(torch.xpu.device_count())]

見落としていたが、Intelのクイックスタートガイドには以下のコマンドを実行せよと書かれていた。参考にしていたこのページだと分かりづらい書き方で(フルパスじゃないのでコピペ実行すると実行できない)ちょっとハマってしまった。

https://github.com/intel/intel-extension-for-pytorch/issues/300#issuecomment-1431218522
結局、これ↑に助けられた。

Bash
source /opt/intel/oneapi/compiler/latest/env/vars.sh
source /opt/intel/oneapi/mkl/latest/env/vars.sh

この2つのコマンドを実行して初めて使えるようになるらしい。

このコマンドを実行してもなお、以下のエラーが出る。libiomp5.soが見つからないとかいうエラーだけど、さっき入れたintel-basekitには含まれていないのか…?自動で入るんじゃないのか…?
torchvisionは使うだろうし直したいが、無視してもいいっぽい的なメッセージが出てくるので無視することにする。torchvision.ioってStable Diffusionで使うんだろうか…?

torchvision.ioが無いことの警告メッセージ
Bash
sh: 1: source: not found
/home/user/.cache/pypoetry/virtualenvs/quickdiffusion-QB0ctf98-py3.10/lib/python3.10/site-packages/torchvision/io/image.py:13: UserWarning: Failed to load image Python extension: ''If you don't plan on using image functionality from `torchvision.io`, you can ignore this warning. Otherwise, there might be something wrong with your environment. Did you have `libjpeg` or `libpng` installed before building `torchvision` from source?
  warn(
Traceback (most recent call last):
  File "/home/user/dev/python/flask-ai/quick_diffusion/files/test.py", line 4, in <module>
    import intel_extension_for_pytorch as ipex
  File "/home/user/.cache/pypoetry/virtualenvs/quickdiffusion-QB0ctf98-py3.10/lib/python3.10/site-packages/intel_extension_for_pytorch/__init__.py", line 94, in <module>
    from .utils._proxy_module import *
  File "/home/user/.cache/pypoetry/virtualenvs/quickdiffusion-QB0ctf98-py3.10/lib/python3.10/site-packages/intel_extension_for_pytorch/utils/_proxy_module.py", line 2, in <module>
    import intel_extension_for_pytorch._C
ImportError: libiomp5.so: cannot open shared object file: No such file or directory

どうやらlibiomp5.soこのGitHubリポジトリにあるらしい。が、リポジトリのオーナー(Intel)がリポジトリをアーカイブにしてて、どうやら開発は止まったままになっているらしい。他に現役のリポジトリがあるのか?

どうやら/opt/intel/oneapi/compiler/以下にファイルがあるっぽいというのがこの記事で分かったので見てみる。
/opt/intel/oneapi/compiler/2024.0/libにファイルがあった。

Bash
libiomp5.a
libiomp5.dbg
libiomp5.so
libiomp5_db.so
libiompstubs5.a
libiompstubs5.so

こんな感じでファイルがあったので、これっぽい。ただ、なぜ存在するのにリンクされてないのかが謎。こういうもんなの…?
見つかったとはいえ、どうやってリンクを貼ればいいかが不明すぎる。

ChatGPTに聞いてみたところ、テストプログラムに書いてたos.system("source /opt/intel/oneapi/setvars.sh")はそこに書いても意味ないとのことだったので、シェルからコマンド(source /opt/intel/oneapi/setvars.sh)としてテストプログラムの実行前に実行してみた。
すると、以下のような出力が現れた。

source /opt/intel/oneapi/setvars.shの出力
Bash
:: initializing oneAPI environment ...
   bash: BASH_VERSION = 5.1.16(1)-release
   args: Using "$@" for setvars.sh arguments: 
:: advisor -- latest
:: ccl -- latest
:: compiler -- latest
:: dal -- latest
:: debugger -- latest
:: dev-utilities -- latest
:: dnnl -- latest
:: dpcpp-ct -- latest
:: dpl -- latest
:: ipp -- latest
:: ippcp -- latest
:: mkl -- latest
:: mpi -- latest
:: tbb -- latest
:: vtune -- latest
:: oneAPI environment initialized ::

これが必要だったのか~~~イケるか~~~??と思いながらテストプログラムを実行したら、今度は同じような見つからないエラーで、別のファイルが見つからないとエラーが出た。エラーは以下。

libze_loader.so.1が見つからないエラー
Bash
/home/user/.cache/pypoetry/virtualenvs/quickdiffusion-QB0ctf98-py3.10/lib/python3.10/site-packages/torchvision/io/image.py:13: UserWarning: Failed to load image Python extension: ''If you don't plan on using image functionality from `torchvision.io`, you can ignore this warning. Otherwise, there might be something wrong with your environment. Did you have `libjpeg` or `libpng` installed before building `torchvision` from source?
  warn(
Traceback (most recent call last):
  File "/home/user/dev/python/flask-ai/quick_diffusion/files/test.py", line 4, in <module>
    import intel_extension_for_pytorch as ipex
  File "/home/user/.cache/pypoetry/virtualenvs/quickdiffusion-QB0ctf98-py3.10/lib/python3.10/site-packages/intel_extension_for_pytorch/__init__.py", line 94, in <module>
    from .utils._proxy_module import *
  File "/home/user/.cache/pypoetry/virtualenvs/quickdiffusion-QB0ctf98-py3.10/lib/python3.10/site-packages/intel_extension_for_pytorch/utils/_proxy_module.py", line 2, in <module>
    import intel_extension_for_pytorch._C
ImportError: libze_loader.so.1: cannot open shared object file: No such file or directory

このlibze_loader.so.1というのは、Intel Level Zeroとかいう技術の一部らしい。が、今回はそもそもマシン上に存在してすらいない(find /home -iname libze_loader.so.1で検索したがヒット0)。なにそれ…
しょうがないので、ネット上にあるパッケージをダウンロードしてきて、手動で入れることにした
libze_loader.so.1が含まれていると思われるパッケージがあるのがこのGitHubリポジトリ
https://github.com/oneapi-src/level-zero/
とりあえず、最新版で不具合が出たらだるいので、少し古めのv1.15.13にしてみたhttps://github.com/oneapi-src/level-zero/releases/download/v1.15.13/

Bash
wget https://github.com/oneapi-src/level-zero/releases/download/v1.15.13/level-zero_1.15.13+u22.04_amd64.deb

.debファイルをダウンロードし、そこでsudo apt-get install level-zero_1.15.13+u22.04_amd64.debをしたところ、なぜかE: Unable to locate package level-zero_1.15.13+u22.04_amd64.debというエラーが。何が悪いのか全くわからないが、ググるとgdebiを使えばいいという情報があったので試す。
https://mell0w-5phere.net/jaded5phere/2019/09/11/local-deb-install/
上記記事の通りに

Bash
sudo apt-get install gdebi

gdebiをインストールし、その後

Bash
sudo gdebi level-zero_1.15.13+u22.04_amd64.deb

で無事パッケージをインストールできた。なんでパッケージインストールにもめんどくさい手順を踏まねばならないのか…

この手順のあと、同じようにテストプログラムを実行すると、以下のようになり、とりあえずIntel® Extension for TensorFlowはインストールできたようだ。ただ、肝心のTorchvisionの警告は消えていないので、どうやらそれはまた別途対処が必要らしい…

Bash
/home/user/.cache/pypoetry/virtualenvs/quickdiffusion-QB0ctf98-py3.10/lib/python3.10/site-packages/torchvision/io/image.py:13: UserWarning: Failed to load image Python extension: ''If you don't plan on using image functionality from `torchvision.io`, you can ignore this warning. Otherwise, there might be something wrong with your environment. Did you have `libjpeg` or `libpng` installed before building `torchvision` from source?
  warn(
2.1.0a0+cxx11.abi
2.1.10+xpu

まとめ

Ubuntuを起動させたらpoetry shellで仮想環境に入る度に、最初に毎回以下の3つのコマンドを実行する。

Bash
source /opt/intel/oneapi/compiler/latest/env/vars.sh
source /opt/intel/oneapi/mkl/latest/env/vars.sh
source /opt/intel/oneapi/setvars.sh
コピペ用
Bash
source /opt/intel/oneapi/compiler/latest/env/vars.sh && source /opt/intel/oneapi/mkl/latest/env/vars.sh && source /opt/intel/oneapi/setvars.sh

無いファイルがあったらとりあえず入れてみる。
更新中…

でじぃでじぃ

TorchVisionは諦めたものの、どういうわけか色々と追加でエラーが出てきたので色々入れた。覚えている範囲で書き残しておく。

libGL.so.1

さぁAIモデルを使おうと思ったら出たエラー。

Bash
ImportError: libGL.so.1: cannot open shared object file

https://cocoinit23.com/docker-opencv-importerror-libgl-so-1-cannot-open-shared-object-file/
入ってない場合があるらしい。以下のコマンドで解決。

Bash
sudo apt-get install libgl1-mesa-dev

lzma

https://qiita.com/shinsaka/items/3df64c938f7cca4ae615
どうやらWSL2とかでPythonを使っているとあることらしい。一旦pyenvでPythonをアンインストールする。

sudo apt-get install liblzma-dev

その後、上記コマンドでlzmaをインストールしてから、再度pyenvでPythonをインストールして解決。