2023年11月15日水曜日

MySQL で csv 出力する方法

MySQL で csv 出力する方法

概要

into outfile はいろいろと面倒なので into outfile を使わずに csv にする方法を紹介します
db_name と table_name は自身の環境に合わせて変更してください
またカラム名に予約語が入っていることを考慮して内部で生成している SQL をバッククオートで囲んでいます

環境

  • MySQL 5.6.22

SQL

SET
  @VTable = 'table_name'
;
SET
  @VAllCols = CONCAT('SELECT CONCAT(',(
      SELECT
        CONCAT('\`',(
            SELECT
              GROUP_CONCAT(COLUMN_NAME SEPARATOR '\`,\', \',\`')
            FROM
              information_schema.columns
            WHERE
              TABLE_NAME = @VTable
            GROUP BY
              table_name
          ), '\`) FROM ', @VTable, ';')
    ))
;
PREPARE stmt
FROM
  @VAllCols
;
EXECUTE stmt
;
DEALLOCATE PREPARE stmt
;

シェルから1行で実行したい場合

mysql -u user_name -p db_name -h 192.168.100.1 -e "SET @VTable = 'table_name'; SET @VAllCols = CONCAT('SELECT CONCAT(',(SELECT CONCAT('\`',(SELECT GROUP_CONCAT(COLUMN_NAME SEPARATOR '\`,\',\',\`') FROM information_schema.columns WHERE TABLE_NAME = @VTable GROUP BY table_name),'\`) FROM ', @VTable, ';'))); PREPARE stmt FROM @VAllCols; EXECUTE stmt; DEALLOCATE PREPARE stmt;"

最後に

自動でカラム情報を抽出しているのでカラムの順番は決められないのでご注意ください

参考サイト

2023年11月14日火曜日

M2 Mac で Audacity に FFmpeg ライブラリが設定できないときの対処方法

M2 Mac で Audacity に FFmpeg ライブラリが設定できないときの対処方法

概要

Homebrew でインストールした ffmpeg を audacity に設定しようとしても「FFmpeg ライブライが見つかりません」というエラーになって設定できませんでした
そんな場合の対処方法を紹介します

環境

  • macOS 14.0
  • Audacity 3.4.1
  • FFmpeg 6.0 (via homebrew)

対処方法

homebrew ではなく audacity から ffmpeg をインストールする

インストール方法はこちら

原因

もしかすると homebrew 版の ffmpeg がうまくインストールできていないのかもしれません
ffmpeg -v など打つと「Library not loaded」となり表示されませんでした

  • brew install lame
  • brew reinstall ffmpeg

を試して見ると audacity 側でもライブラリを読み込んでくれるかもしれません

もしくは以下の再インストール方法も役に立つかもしれません

  • brew uninstall librist --ignore-dependencies
  • brew uninstall mbedtls --ignore-dependencies
  • brew reinstall ffmpeg

最後に

ダメな場合は素直にインストーラを使って ffmpeg をインストールしましょう

2023年11月13日月曜日

(SQLAlchemy) RSA Encryption not supported - caching_sha2_password plugin was built with GnuTLS support

(SQLAlchemy) RSA Encryption not supported - caching_sha2_password plugin was built with GnuTLS support

概要

mysqld 側の設定ではなかったので対処方法を紹介します

環境

  • Ubuntu 22.04
  • MySQL 8.0.34
  • Python 3.11.3
    • SQLAlchemy 2.0.20

対応方法

ユーザを mysql_native_password で作成し直します

DROP USER "user01"@"172.22.%";
CREATE USER "user01"@"172.22.%" IDENTIFIED WITH mysql_native_password BY "xxx";
GRANT ALL PRIVILEGES ON *.* TO "user01"@"172.22.%";

2023年11月12日日曜日

mkdocs-material 超入門

mkdocs-material 超入門

概要

mkdocs-material は Python 製のドキュメントツールです
とりあえず動かすところまでやってみました

環境

  • macOS 14.0
  • Python 3.11.6
    • mkdocs-material 9.4.7

インストール

  • pipenv install mkdocs-material

サイト作成

  • pipenv run mkdocs new mkdocs_test

テーマ設定

  • vim mkdocs_test/mkdocs.yml
site_name: My Docs
theme:
  name: material

ページ追加

  • vim mkdocs_test/docs/test.md
# This is a test page
![image](https://picsum.photos/200/300)

動作確認

  • pipenv run mkdocs serve -f mkdocs_test/mkdocs.yml

最後に

とりあえずページを追加して動作するところまでやってみました
いろいろなプラグインがありブログサイトなども簡単に作ることができるようです

参考サイト

2023年11月11日土曜日

ChatGPT を API から使ってみる

ChatGPT を API から使ってみる

概要

ChatGPT を API からコールしてみました
SDK は Python のものを使っています
OpenAI のアカウントは取得済みの前提になります
なお以下の操作は有料操作になるため課金が発生するので注意しましょう

環境

  • macOS 14.0
  • Python 3.11.6
    • openai 1.1.1

API Key の取得

ここから取得しましょう

「Create new secret key」を押しキーの名称を登録すれば OK です

ライブラリインストール

  • pipenv install openai

キーのエクスポート

自動的に環境変数を拾ってくれるようなので設定します
実行時でも OK です

  • export OPENAI_API_KEY='sk-xxx'

サンプルコード

from openai import OpenAI

client = OpenAI()

completion = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[
        # system の content はチャット全体のコンテキストを設定します、今から質問する概要的な文を設定します
        {"role": "system", "content": "Python の fastapi について質問です。"},
        # user の content は 実際の質問を書きます
        {"role": "user", "content": "dependency の使い方を教えてください。"},
    ],
)

print(completion.choices[0].message)

実行

  • OPENAI_API_KEY=‘sk-xx’ pipenv run python app.py

429 エラーになる場合は

openai.RateLimitError: Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}

素直に支払い方法を登録しましょう
最低でも $5 からのチャージが必要です

最後に

かなり簡単に使うことができます
汎用的な会話でいいのであればもはや自分でモデルを作ってアプリケーション化するのは不要かもしれません (お金を気にしないのであれば

参考サイト

2023年11月10日金曜日

Keras 学習コンテンツ一覧

Keras 学習コンテンツ一覧

概要

Keras の使い方を学びたかったので簡単な使い方から RNN を使った文章生成までやってみました
それぞれ記事にしたので一覧をまとめておきます
また学習した感想など記載します

環境

  • macOS 14.0
  • Python 3.11.6
    • keras 2.14.0
    • tensorflow 2.14.0
    • matplotlib 3.8.1

記事一覧

その他記事

感じたこと

  • Keras の使い方もそうだが numpy の使い方のほうが重要だと感じた
    • numpy で作成したベクトルの扱い方
    • 多次元ベクトル、shape の情報をイメージできるか
    • reshape など
  • Keras のレイヤーを理解し使えるようになるまでは大変そう
    • 入力の数と出力の数を考慮してレイヤーを結合できるか
    • 学習時と評価時でちゃんと適切なテストデータを作成することができるか
  • 学習よりもデータを生成、クリーニングする段階のほうが重要であり難しいコードになりがちな気がする
    • numpy や tf.data を使って学習に必要なフォーマットに落とせるか
    • RNN の場合は「一つずらす」必要があるのでこれをコードで表現できるか
    • numpy や tf.data は最悪使わないでもできるので無理に使わなくてもいい
  • コードをリファクタリングしたほうが理解が深まる
    • なぜかすべての紹介されているコードがすべて一枚ペラのコードばかりでクラス化されていたり冗長部分の排除ができていなかった
    • TestData, Model, Test など必要な機能、役割ごとに分割して管理すると何をやっているかもわかりやすい気がした
    • 自分が紹介してるコードはクラス化されています
  • ハイパーパラメータについて
    • こればかりは数学の知識や経験が大事になってくる
    • サンプルの値をそのまま使ってもいいがハイパーパラメータの変化による効果や仕組みを理解してないと Keras を理解したとは言えないと思う
    • それでも王道を使っておけば深層学習自体のポテンシャルの高さである程度の精度は出すことができる (それ以上を求める場合には数学の知識を含めハイパーパラメータの扱いが必要な気がする
  • 評価関数、損失関数など
    • ここも経験や数学の知識によって精度に大きく影響する
    • 一流を求めるなら自作の関数の作成と理解が必要
    • それでもデフォルトの関数を使えばそれなりの評価が出てしまう

2023年11月9日木曜日

Keras + RNN の文章生成チュートリアルをリファクタしてみた

Keras + RNN の文章生成チュートリアルをリファクタしてみた

概要

過去にやった文章生成のチュートリアルをリファクタして更に理解を深めてみました

環境

  • macOS 14.0
  • Python 3.11.6
    • keras 2.14.0
    • tensorflow 2.14.0
    • matplotlib 3.8.1

サンプルコード

import os
from dataclasses import dataclass
from typing import Optional

import numpy as np
import tensorflow as tf
from keras.src.callbacks import History
from tensorflow.keras.callbacks import ModelCheckpoint
from tensorflow.keras.layers import GRU, Dense, Embedding
from tensorflow.keras.losses import sparse_categorical_crossentropy
from tensorflow.keras.models import Sequential
from tensorflow.keras.utils import get_file


# テストデータを管理するクラス
class TestData:
    # 文章を分割する長さ
    SEQ_LENGTH = 100
    # シャッフル用のバッファサイズ
    BUFFER_SIZE = 10000
    # シャッフル後の分割単位
    BATCH_SIZE = 64

    def __init__(self) -> None:
        # テキストデータのダウンロード
        self.__download()
        # 各種文字と文字インデックスの組み合わせの辞書と配列を作成
        self.char2idx = self.__gen_char2idx()
        self.idx2char = self.__gen_idx2char()
        # テキスト情報を数字情報のインデックス番号の配列に変換
        self.text_as_int = np.array([self.char2idx[c] for c in self.text])
        # TensorSliceDataset の作成、配列で要素は tf.Tensor に変換されたテキスト情報を数字情報のインデックス番号がある
        self.char_dataset = tf.data.Dataset.from_tensor_slices(self.text_as_int)
        # 100 文字ごとに分割して TensorSliceDataset を再度作成、BatchDataset になる
        # ここで説明変数と目的変数が生成される
        self.sequences = self.char_dataset.batch(
            self.SEQ_LENGTH + 1, drop_remainder=True
        )
        # 各 100 文字ごとの文章情報を1文字づらした [input: tf.Tensor, target: tf.Tensor] 配列を作ります、全文章分 map で繰り返して作ります
        dataset = self.sequences.map(self.shift)
        # シャルフルし再度 64 個ごとにまとめます、BatchDataset になる
        self.dataset = dataset.shuffle(self.BUFFER_SIZE).batch(
            self.BATCH_SIZE, drop_remainder=True
        )

    @property
    def vocab(self) -> list:
        # 文字を分割して辞書順に並び替えたリストを生成
        return sorted(set(self.text))

    def __download(self):
        path_to_file = get_file(
            "shakespeare.txt",
            "https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt",
        )
        self.text = open(path_to_file, "rb").read().decode(encoding="utf-8")

    def __gen_char2idx(self) -> dict:
        # 文字に対応した数字のインデックス番号の組み合わせを生成
        return {u: i for i, u in enumerate(self.vocab)}

    def __gen_idx2char(self) -> np.ndarray:
        # 文字を分割して辞書順に並び替えたリストを ndarray 形式に変換
        return np.array(self.vocab)

    def shift(self, chunk: tf.raw_ops.BatchDataset):
        # RNN 学習のために1文字づらしたシーケンスを作成します
        # :-1 は最後から1文字抜いた先頭からすべての文字 (説明変数)
        independent = chunk[:-1]
        # 1: は先頭から1文字抜いた最後までのすべての文字 (目的変数)
        dependent = chunk[1:]
        return independent, dependent


# RNN モデルを管理するクラス
class RNNModel:
    EMBEDDING_DIM = 256  # 埋め込み層の次元数
    RUN_UNITS = 1024  # RNN ユニット数
    model: Sequential

    def __init__(self, vocab_size: int, batch_size) -> None:
        self.model = Sequential()
        self.vocab_size = vocab_size
        self.batch_size = batch_size

    def build(self, batch_size: Optional[int] = None, new: bool = False):
        if batch_size is None:
            bs = self.batch_size
        else:
            bs = batch_size
        if new:
            self.model = Sequential()
        # 埋め込み層、固定次元の密ベクトルに変換するレイヤー
        self.model.add(
            Embedding(
                self.vocab_size,
                self.EMBEDDING_DIM,
                batch_input_shape=[bs, None],
            )
        )
        # RNN 層、今回は GRU レイヤーを使う
        self.model.add(
            GRU(
                self.RUN_UNITS,
                return_sequences=True,
                stateful=True,
                recurrent_initializer="glorot_uniform",
            )
        )
        # 出力層
        self.model.add(Dense(self.vocab_size))

    def compile(self) -> None:
        self.model.compile(optimizer="adam", loss=Loss().keras_scc)

    def show(self):
        self.model.summary()

    def train(
        self,
        dataset: tf.raw_ops.BatchDataset,
        epochs=10,
        callbacks=[],
    ) -> History:
        history = self.model.fit(dataset, epochs=epochs, callbacks=callbacks)
        return history

    def save(self, file_name="my_model"):
        self.model.save(file_name)

    def rebuild(self, checkpoint_dir: str = "./training_checkpoints"):
        # テスト時の入力のバッチサイズは1になるので batch_size=1 で再度ビルドする
        self.build(batch_size=1, new=True)
        self.model.load_weights(tf.train.latest_checkpoint(checkpoint_dir))
        self.model.build(tf.TensorShape([1, None]))


# 損失関数を管理するクラス
class Loss:
    def keras_scc(self, y_true, y_pred):
        # sparse_categorical_crossentropy をカスタム
        return sparse_categorical_crossentropy(y_true, y_pred, from_logits=True)


# モデルを再度構築するので1回目のモデルを保存するためのコールバック
class Callback:
    @classmethod
    def save_model(cls) -> ModelCheckpoint:
        checkpoint_dir = "./training_checkpoints"
        checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt_{epoch}")
        return ModelCheckpoint(filepath=checkpoint_prefix, save_weights_only=True)


# 実際にモデルを評価するためのクラス
@dataclass
class Result:
    text: str

    def show(self):
        print(self.text)


class Test:
    # 1.0 より大きいほど意外なテキストを生成する、1.0 は予測したそのままの値になる
    TEMPERRATURE = 1.0
    # 生成する文字数
    NUM_GENERATE_CHAR = 1000

    def __init__(
        self, model: RNNModel, test_data: TestData, start_string: str = "ROMEO: "
    ) -> None:
        self.model = model
        self.test_data = test_data
        self.start_string = start_string
        # start_string を数字のインデックス配列に変換
        input_eval = [self.test_data.char2idx[char] for char in start_string]
        self.input_eval = tf.expand_dims(input_eval, 0)

    def run(self) -> Result:
        self.model.model.reset_states()
        generated_text = []
        for _ in range(self.NUM_GENERATE_CHAR):
            # 文字の予測
            predictions = self.model.model(self.input_eval)
            # サイズが1の次元を削除し次元数を減らす
            predictions = tf.squeeze(predictions, 0)
            # カテゴリー分布をつかってモデルから返された文字のインデックス番号を予測
            predictions = predictions / self.TEMPERRATURE
            # 最後の要素が入力に対して予測された文字のインデックス番号
            predicted_id = tf.random.categorical(predictions, num_samples=1)[
                -1, 0
            ].numpy()
            # 過去の隠れ状態とともに予測された文字をモデルへのつぎの入力として渡す
            self.input_eval = tf.expand_dims([predicted_id], 0)
            # インデックス番号から文字への変換と結果の格納
            generated_text.append(self.test_data.idx2char[predicted_id])
        return Result(text=self.start_string + "".join(generated_text))


if __name__ == "__main__":
    # テストデータ作成
    test_data = TestData()
    # モデル生成
    model = RNNModel(len(test_data.vocab), TestData.BATCH_SIZE)
    model.build()
    model.compile()
    # # モデルの訓練
    model.train(test_data.dataset, callbacks=[Callback.save_model()])
    # モデルの評価
    model.rebuild()
    model.show()
    test = Test(model, test_data)
    result = test.run()
    # 結果を表示
    result.show()

ちょっと解説

テストデータは単純なテキストを使います
テキスト情報を1文字ずつ分解し数字とのマッピング情報を作成しテキストをすべて数値化することで学習データを作成します
文字情報の数値化は自然言語ではよくある手法になります

データをシャッフルしたりバッチサイズで分割するのは Keras で学習させる際のフォーマットに変換していると理解していますがこの辺りの手法を自然に使いこなせるレベルにならないとダメかなと思います

モデルの生成は Embedding -> GRU -> Dense とシンプルです
Embedding は自然言語の学習ではほぼ必須です
GRU (Gated Recurrent Unit) は RNN の一種です
モデル学習時にチェックポイントを保存しています
評価する際に入力をシンプルにしたいので学習時のバッチサイズ64を再度チェックポイントからモデルをビルドしてバッチサイズを1にするためです

予測した値はそのまま使わずカテゴリー分布という手法を使って再度計算させています (正直理由は不明です

リファクタリング不足点

  • TestData をもう少しリファクタリングしたほうがいい
    • データの生成を init ではなくそれぞれの関数にしたほうがいい
    • 説明変数と目的変数を別のクラスで管理した方がいい
  • Model の model の管理をリファクタリングしたほうがいい
    • build で新規 or 既存の書き換えを制御するよりかは ModelFactory を作って model を個別に生成できるような仕組みにするといいかも
  • Test を柔軟にしたほうがいい
    • いろいろなデータでテストできるようにしたほうがいい
    • 生成文字列サイズなども可変にできるといい
    • 何をやっているかわからないところがあるのでコメントで補完したい

最後に

Keras を使った学習方法や流れはだいたい把握できましたがフルスクラッチでゼロから numpy や keras を使ってモデルを作るにはまだまだ学習が足りないと感じました

numpy で生成したベクトルの扱い方や keras のレイヤーの生成方法やレイヤーに対する入出力の制御、最適化あたりを駆使できるようにならないとオリジナルなモデルを作るのは難しいなと感じました

参考サイト