tottocoslowlifer / tide

2024年のプロジェクト
0 stars 0 forks source link

機械学習アルゴリズムの実装に向けた準備 #7

Open victor-von-pooh opened 7 months ago

victor-von-pooh commented 7 months ago

4月〜5月にかけて

機械学習のアルゴリズムを実装し, 潮位のデータを用いて未来の数値を予測する.

準備

実験を行う上での準備を整える手順を示す.

victor-von-pooh commented 7 months ago

@tottocoslowlifer まずは, 現段階で全ての README.md ファイルに記載漏れが無いかを確認し, requirements.txt を更新した上で,

データ分析で行ったものと同じようにして preprocessed_df を作成し, 新たに csv ファイルを作成するスクリプトを tide/scripts に用意する

こちらを終わらせましょう.

tottocoslowlifer commented 7 months ago

README.mdファイルに追記する内容は特にありませんでした.

requirements.txtに関してですが,今回,新たに追加したものは

from scipy import signal
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from statsmodels.tsa.seasonal import STL
from statsmodels.tsa.stattools import adfuller
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

です. scipy, sklearn, statsmodelsのうち,書く必要があるものはどれでしょうか.

numpypandasのように直接,importと書いていないため,どうなのかが気になりました.

victor-von-pooh commented 7 months ago

すみません, 完全に見落としていましたが,

from scipy import signal

こちらがどこにも使われていないようなのでまず notebook から削除してください.

scipy, sklearn, statsmodelsのうち,書く必要があるものはどれでしょうか. numpyやpandasのように直接,importと書いていないため,どうなのかが気になりました.

外部ライブラリなので全てです. import xx は文字通りライブラリ xx そのものをインポートするものですが, from xx import yyxx にある yy をインポートするという意味であり, そもそも xx が環境下にない場合はエラーが起きてしまいます.

今回,

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from statsmodels.tsa.seasonal import STL
from statsmodels.tsa.stattools import adfuller
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

となっているので, Scikit-LearnStatsmodels が必要になります.

それぞれ,

pip3 install scikit-learn
pip3 install statsmodels

でインストールできると書かれているので, requirements.txt への追記は

scikit-learn
statsmodels

になります.

tottocoslowlifer commented 7 months ago

かしこまりました.ありがとうございます. notebookおよびrequirements.txtをコミットいたしました.

victor-von-pooh commented 7 months ago

確認が取れました. 引き続き,

データ分析で行ったものと同じようにして preprocessed_df を作成し, 新たに csv ファイルを作成するスクリプトを tide/scripts に用意する

こちらの対応をよろしくお願い致します.

tottocoslowlifer commented 7 months ago

ご確認ありがとうございます. preprocessed_csv_convert.pyを新たに作成いたしました. よろしくお願いいたします.

victor-von-pooh commented 7 months ago

確認が遅くなってしまい, 大変申し訳ございませんでした. また, 実施事項に不備があり, 伝え損ねたことがありましたので, ご対応のほどよろしくお願い致します.

データ分析で行ったものと同じようにして preprocessed_df を作成し, 新たに csv ファイルを作成するスクリプトを tide/scripts に用意する

$$\downarrow \quad \downarrow \quad \downarrow$$

データ分析で行ったものと同じようにして preprocessed_df を作成し, メタ情報を使った重回帰分析による予測で欠損値を補完した後の DataFrame を新たに csv ファイルとして保存するスクリプトを tide/scripts に用意する

tottocoslowlifer commented 7 months ago

かしこまりました. 修正が完了したので,ご確認の程よろしくお願いいたします.

victor-von-pooh commented 7 months ago

@tottocoslowlifer ご対応いただきありがとうございます. 以下コメントになります.

コメント

preprocessed_csv_convert.py について, 以下の変更を加えてください.

tottocoslowlifer commented 7 months ago

ありがとうございます. 再度コミットいたしました.

victor-von-pooh commented 7 months ago

@tottocoslowlifer ありがとうございました. LGTM です.

続いて,

セットアップツールを作成する

こちらに移りたいと思うのですが, この目的があまりわからないと思うので軽くご説明します.

実験管理

各モデルを使った結果の精度を上げるために, 実験を繰り返してパラメータを調整しなければいけない. $\rightarrow$ 各実験の詳細情報を記録する必要がある.

tide/experiment_tools フォルダ

実験において必要になるセットアップ関連のツールを管理するフォルダ.

それぞれのモデルの実験において, これらを使うことを徹底する.

config

いきなりセットアップツールの作成は難しい. $\rightarrow$ まずは必要なものを検討したい.

また, 乱数などは指定を出来るようにしたい. このような場合, モデルごとに config(configuration) というものを作る. config ファイルを参照し, モデルのパラメータの調節を行う. こちらの感覚にまずは慣れておきたい.

やってみること

ここで行われているプログラムの流れを全て理解するところから始める.

tottocoslowlifer commented 7 months ago

configファイルの必要性

実験を繰り返し行う上で,

を明示するために,configファイルを作成する.

各モデルの実験ごとに別ファイルを作成するが,まずは標準的なものとしてtide/config/default/test.jsonファイルを用意している.

tide/scripts/logs.pyファイルについて

import json
import logging
from logging import getLogger, FileHandler, Formatter
import subprocess

def set_logging(cfg):
    logger = getLogger(__name__) # loggerオブジェクト "__name__" の宣言
    logger.setLevel(logging.INFO) # ログレベルをINFOに設定
    handler = FileHandler(cfg["log"]["log_file"], mode='w') # ここでは "../outputs/test.log" を書き込みモードで開く
    formatter = Formatter(cfg["log"]["log_formatter"]) 
    handler.setFormatter(formatter) # handlerのformatは "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    logger.addHandler(handler) # loggerにhandlerをセット
    return logger

filename = "../config/default/test.json"
with open(filename) as f:
    cfg = json.load(f) # グローバルなオブジェクトとしてのcfgを宣言

logger = set_logging(cfg) # グローバルなオブジェクトとしてのloggerを,cfgを元に宣言

git_info = "commit id: "
# $ git rev-parse HEAD の出力結果をgit_infoに追加(最新のコミットハッシュ(修正内容と修正者の情報)が記録される)
git_info += subprocess.check_output(
    ['git', 'rev-parse', 'HEAD']
).decode().strip()

# 以下,ログレベルのINFOを超えているため出力される
logger.info(git_info) # 実験を行った時点のレポジトリの状態
logger.info(f"username: {cfg['name']}") # 実験を行った時点のGitHubのコミットID
logger.info(cfg["greeting"] * cfg["reps"]) # cfg内のgreetingをrepの数だけ繰り返し

長くなってしまいましたが,目を通していただけたら幸いです. また, greetingの意義をよくわかっていないのですが,実際にどういった用途に用いるものか,教えていただけたら幸いです.

victor-von-pooh commented 7 months ago

@tottocoslowlifer ご対応ありがとうございます. 以下コメントになります.

コメント

コメントアウト内容

tide/scripts/logs.py ファイルのコメントアウトについて, 「#」の後にスペースを1つ空けているのは良いです◎ Python のコメントアウトの正しい記法になります.

では, そのコメントアウトについてですが,

handler = FileHandler(cfg["log"]["log_file"], mode='w') # ここでは "../outputs/test.log" を書き込みモードで開く

こちらについて, mode='w' (正しくは mode="w" です, ごめんなさい)にしている理由としては, tide/outputs/test.log ファイル上で, 過去の log を上書きするためにしています(ここを指定しないと以前に書かれたものの続きで書かれる). この .log ファイルは今後実験ごとに個別で作成する予定なのでどちらでも本当は構いません.

質問について

また, greetingの意義をよくわかっていないのですが,実際にどういった用途に用いるものか,教えていただけたら幸いです.

ご質問ありがとうございます. 意義は, ありません.

今回のワークのポイントは次の2つです.

2点目について, tide/config/default/test.json という json の config ファイルを読み込み, そこから値を抽出するということをしていました. ここで気にするべきことは, json 形式の config ファイルを扱う際に, そこに書かれた key と value の対応と使える型の確認をすることです. json のファイルは Python の辞書型と同様に扱えるので, 読み込んでしまえばあとは簡単ではあります. しかし, value で使える型は str 型のみなのか, int 型や float 型としてそのまま読み込めるのか, 他に扱えるオブジェクトはあるのかなどを考える必要はありますね. 今回,

logger.info(cfg["greeting"] * cfg["reps"]) # cfg内のgreetingをrepの数だけ繰り返し

こちらの1行で str 型である cfg["greeting"]cfg["reps"] をかけるということが出来ています. すなわち, cfg["reps"] は int 型でそのまま扱えることがわかります.

実際にプラクティスに入る際には, 例えば Scikit-Learn の決定木回帰モデルを扱うことを考えると, 引数には

スクリーンショット 2024-04-05 21 04 17

これだけのパラメータがあります. 型がバラバラであり, config でしっかりと管理しなければいけません. これを意識してもらうことが狙いでした.

tottocoslowlifer commented 7 months ago

早速のご返信ありがとうございます.

tide/outputs/test.log ファイル上で, 過去の log を上書きするためにしています

今調べたところ,デフォルトの"a"(コードを実行する度に追記される)に対して,"w"があるのですね.

また,質問に対してのご返答もありがとうございます.疑問が解消されました.

victor-von-pooh commented 7 months ago

@tottocoslowlifer

今調べたところ,デフォルトの"a"(コードを実行する度に追記される)に対して,"w"があるのですね.

デフォルト値がどうなっているのかご自身で確認する姿勢, 大変素晴らしいです◎ それでは以下の2点についてのチュートリアルが済んだという理解でよろしいでしょうか?

こちらがよろしければ, 早速

セットアップツールを作成する

こちらに移ってみましょう. まずは tide/scripts/logs.py, tide/config/default/test.json, tide/outputs/test.log の3つのファイルを削除しましょう. そして, tide/experiment_tools というフォルダを作成しましょう. このフォルダ下に

の4つの空のファイルを用意してください. ここまで出来たらまたご一報ください.

tottocoslowlifer commented 7 months ago

ありがとうございます. コミットが完了いたしました.

victor-von-pooh commented 7 months ago

@tottocoslowlifer 迅速なご対応ありがとうございます.

それではまず, tide/experiment_tools/start_logging.py の中身を記述してみましょう. ここでは, get_logger() 関数を定義しましょう. 引数は cfg とし, logger という変数を返り値に持つように log に残すべきだと思われるものの準備をしましょう. なお, ここでは get_logger() 関数と必要なライブラリの import 以外で記述するものはありません.

tottocoslowlifer commented 7 months ago

コミットいたしました. このようなもので意図と合っていますでしょうか.

victor-von-pooh commented 7 months ago

はい, プログラムとしては完璧です. 一応型の宣言をしておきましょう.

def get_logger(cfg: dict) -> logging.Logger:

とだけ書き換えておきましょうか. これだけ変更を加えて以下の対応をよろしくお願い致します.

tide/experiment_tools/set_random_seed.pyfix_seed() 関数を作りましょう. こちらも tide/experiment_tools/start_logging.py 同様関数を作るだけにします. 参考記事を見て, 必要そうなものを書いてみましょう.

tottocoslowlifer commented 7 months ago

コミットいたしました. 記事によると,TensorFlowに関しては,GPU周りの情報が少ないとのことなので省きました.

victor-von-pooh commented 7 months ago

@tottocoslowlifer お疲れ様です. 以下コメントになります.

コメント

返信内容について

記事によると,TensorFlowに関しては,GPU周りの情報が少ないとのことなので省きました.

OK です. おそらく TensorFlow はこれからも使わないと思うので大丈夫です.

PyTorch について

現在, PyTorch を使っていない状況(今後は使う予定あり)ですので, tide/requirements.txttorch と入れておいてください.

関数外について

このファイル自体の実行はしない為, 最後の2行はいらないです. config ファイルに seed 値を設定するため, ここでは使いません.

tottocoslowlifer commented 7 months ago

ありがとうございます. 再度コミットいたしました.

victor-von-pooh commented 7 months ago

@tottocoslowlifer ありがとうございます. 追加で, こちらも引数は型の指定をしましょう.

def fix_seed(seed: int):
tottocoslowlifer commented 7 months ago

対応が完了いたしました. よろしくお願いいたします.

victor-von-pooh commented 7 months ago

@tottocoslowlifer ありがとうございました. それでは続いて, tide/experiment_tools/set_up.py を書いていきましょう. ここでは, log を残す最初のステップになります. どのモデルを使用しても, 基本的に中身は変わらない部分になると思います.

まずは, 実験を行う上でどのようなことを log に残すべきかを箇条書きで書いてみましょう.

tottocoslowlifer commented 7 months ago

お疲れ様です. 実験を行った時点の ・日時 ・レポジトリの状態 ・GitHubのコミットID では不十分でしょうか.よろしくお願いいたします.

victor-von-pooh commented 7 months ago

@tottocoslowlifer お疲れ様です.

実験を行った時点の ・日時 ・レポジトリの状態 ・GitHubのコミットID では不十分でしょうか.

良いところまで考えられていると思います.

日時について

これは config の log.log_formatter にて %(asctime)s を指定しているため, 各 log においてそれが実行された時間が自動で出力されるので, わざわざ手動で加える必要はありません.

リポジトリの状態

これは, 「変更などを加えることで出力の結果が変わってくるため, どのような条件で実験を行ったかがわかるようにしたい」という意図だと解釈しています. 一方で, GitHub のコミット ID を URL に加えることで, その時のリポジトリの情報は得られるため実質不要です.

https://github.com/tottocoslowlifer/tide/tree/9d642e7aaf3155fc42014e21352b1c9256de6dc1

GitHub のコミット ID について

こちらは必要です.

さらに必要なもの

tottocoslowlifer commented 7 months ago

ありがとうございます.

実験を行った時点の ・日時 ・レポジトリの状態 ・GitHubのコミットID ・実験者のGitHub のアカウント名 ・実行環境 ということでよろしいでしょうか.

victor-von-pooh commented 6 months ago

@tottocoslowlifer いえ, 次の3つです.

日時については, tide/experiment_tools/start_logging.py で作った get_logger() 関数9行目,

formatter = Formatter(cfg["log"]["log_formatter"])

ここで config の log.log_formatter を参照していることがわかります. config の該当部分には "%(asctime)s - %(name)s - %(levelname)s - %(message)s" と入れています. この %(asctime)s には, 残す log ごとに実行した時の日時が自動で記録されるようになります.

2024-04-06 18:39:48,490 - experiment_tools.start_logging - INFO - commit id: 136d189e646e86d1fdba39d3bf3d615148fb6579

リポジトリの状態については, GitHub のコミット IDを, https://github.com/tottocoslowlifer/tide/tree/hogehogehogehoge の部分に入れ, リンク先を確認するとその時点でのリポジトリの状態が確認できます. すなわち, "GitHub のコミット ID" = "その時点のリポジトリの状態の key" ということです.

tottocoslowlifer commented 6 months ago

理解いたしました. ありがとうございます.

victor-von-pooh commented 6 months ago

それではまず, GitHub の必要な情報を log に残す logger を引数および返り値にもつ get_git_info() 関数のドラフトをコメント内に書いてみましょう.

tottocoslowlifer commented 6 months ago
def get_git_info(logger) -> logging.Logger:
    logger = get_logger(cfg)

    git_info = "commit id: "
    git_info += subprocess.check_output(
        ['git', 'rev-parse', 'HEAD']
    ).decode().strip()
    logger.info(git_info)
    logger.info(f"username: {cfg['name']}")
    return logger

でいかがでしょうか.

cfgは関数外で

filename = "..."
with open(filename) as f:
    cfg = json.load(f)

logger = get_logger(cfg)

のように宣言するイメージでいます.

victor-von-pooh commented 6 months ago

@tottocoslowlifer ありがとうございます. 補足もわかりやすいです.

1点, logger.info(f"username: {cfg['name']}") については, config で指定するのは大変なので自動で取得できるようにしましょう. ヒントはその上の

subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode().strip()

です. これは,

git rev-parse HEAD

のコマンドをターミナル上で実行した結果を反映しています.

追記

関数冒頭1行は不要です.

def get_git_info(logger) -> logging.Logger:
    logger = get_logger(cfg)
tottocoslowlifer commented 6 months ago

ご確認ありがとうございます.

def get_git_info(logger) -> logging.Logger:
    git_info = "commit id: "
    git_info += subprocess.check_output(
        ["git", "rev-parse", "HEAD"]
    ).decode().strip()

    git_name = "username: "
    git_name += subprocess.check_output(
        ["git", "config", "user.name"]
    ).decode().strip()

    os_info = subprocess.check_output(["sw_vers"]).decode().strip()

    logger.info(git_info)
    logger.info(git_name)
    logger.info(os_info)
    return logger

訂正し, 実行環境の情報も追加してみたので,ご確認の程よろしくお願いいたします.

victor-von-pooh commented 6 months ago

ありがとうございます. 確認が取れました.

OS の情報についても取得していただいているのですが, それはまた別途関数を用意してみましょう. まずは GitHub の情報を取得するだけの get_git_info() 関数を実装し, commit しましょう.

tottocoslowlifer commented 6 months ago

かしこまりました. コミットが完了いたしました.

victor-von-pooh commented 6 months ago

OKです.

それでは OS の情報を取得する get_os_info() 関数を同様に書いてみましょう. 先の GitHub の log と合わせて次のような出力をイメージしましょう.

2024-04-12 20:06:51,813 - experiment_tools.start_logging - INFO - commit id: 136d189e646e86d1fdba39d3bf3d615148fb6579
2024-04-12 20:06:51,814 - experiment_tools.start_logging - INFO - username: victor-von-pooh
2024-04-12 20:06:51,827 - experiment_tools.start_logging - INFO - OS infomation: 

    OS: Darwin 21.2.0
    Processor: i386
    Machine: x86_64
    Node: akita
    Python Version: 3.9.13

おすすめは platform ライブラリを使うことです.

tottocoslowlifer commented 6 months ago

お疲れ様です.

get_os_info()関数を書いてみました. 引数のloggerに,先ほどのget_git_info()の返り値であるloggerを渡すイメージです.

ご確認の程よろしくお願いいたします.

def get_os_info(logger) -> logging.Logger:
    os_info = "OS information: \n"

    info_dict = {"OS": subprocess.check_output(platform.system_alias()),
                 "Processor": subprocess.check_output(platform.processor()),
                 "Machine": subprocess.check_output(platform.machine()),
                 "Node": subprocess.check_output(platform.node()),
                 "Python Version": subprocess.check_output(
                     platform.python_version())
                 }
    for key, value in info_dict.items():
        os_info += key
        os_info += ": "
        os_info += value
        os_info += "\n"

    logger.info(os_info)
    return logger
victor-von-pooh commented 6 months ago

platform.hogehoge() はコマンドではないので subprocess.check_output() の引数に入れる必要はないです.

tottocoslowlifer commented 6 months ago

ありがとうございます.

def get_os_info(logger) -> logging.Logger:
    os_info = "OS infomation: "

    info_dict = {"OS": platform.system_alias(),
                 "Processor": platform.processor(),
                 "Machine": platform.machine(),
                 "Node": platform.node(),
                 "Python Version": platform.python_version()
                 }
    for key, value in info_dict.items():
        os_info += key
        os_info += ": "
        os_info += value

    logger.info(os_info)
    return logger

で大丈夫でしょうか.

victor-von-pooh commented 6 months ago

概形は悪くはないです. 数ヶ所直すと良いポイントがあります. 内容把握が出来たらチェックをつけてください.

info_dict の書き方

インデントの揃え方があまり良くないです. 基本的に, list や dict などでは, 次のようにします.

    info_dict = {
        "OS": platform.system_alias(),
        "Processor": platform.processor(),
        "Machine": platform.machine(),
        "Node": platform.node(),
        "Python Version": platform.python_version(),
    }

インデントの位置は4つ刻みになるのが基本です. また, 最後の item にもカンマ( , )は打つと良いですね.

platform.system_alias() には引数が必要

こちらは次のような TypeError が表示されてしまいます.

TypeError: system_alias() missing 3 required positional arguments: 'system', 'release', and 'version'

ここは普通に,

"OS": f"{platform.system()} {platform.release()}"

などとすると良いかと思います.

for key, value in info_dict.items(): 下でエラーが生じる

platform.system_alias() の返り値がタプルになるため, 型の不一致から os_info += value ができなくなってしまいます.

提案

def get_os_info(logger: logging.Logger) -> logging.Logger:
    os_info = "\n\n"
    spec = [
        f"\tOS: {platform.system()} {platform.release()}",
        f"\tProcessor: {platform.processor()}",
        f"\tMachine: {platform.machine()}",
        f"\tNode: {platform.node()}",
        f"\tPython Version: {platform.python_version()}"
    ]
    for item in spec:
        os_info += item
        os_info += "\n"

    logger.info(f"OS infomation: {os_info}")
    return logger
tottocoslowlifer commented 6 months ago

ありがとうございます.内容把握が完了いたしました.

加えてなのですが,configファイルがまだないため,実は挙動を確認できないまま,貼り付けてしまいました. 書いたプログラムを試すには,configファイルを仮作成すべきだったのでしょうか.

victor-von-pooh commented 6 months ago

書いたプログラムを試すには,configファイルを仮作成すべきだったのでしょうか.

出来るようであればしておくと良いと思いますが, この後 config を実際に作って確認する工程に入るので, 今は無くても大丈夫です. とりあえず, get_os_info() 関数を追記しましょう.

tottocoslowlifer commented 6 months ago

かしこまりました.コミットいたしました.

victor-von-pooh commented 6 months ago

ありがとうございます. 確認が出来ました. それでは最後のステップですが, 今までの実装プログラムを全て起動して log を作る start_experiment() 関数を tide/experiment_tools/set_up.py ファイル内に実装してみましょう.

次のフレームワークを埋めてください. コメントアウトは消しても良いです.

def start_experiment(cfg: dict) -> logging.Logger:
    # log の起動 ↓↓↓

    # log の起動 ↑↑↑

    # 乱数 seed の固定 ↓↓↓

    # 乱数 seed の固定 ↑↑↑

    # log に GitHub と OS の環境情報を入れる ↓↓↓

    # log に GitHub と OS の環境情報を入れる ↑↑↑

    return logger
tottocoslowlifer commented 6 months ago

お疲れ様です. 書いてみたので,ご確認の程よろしくお願いいたします.

def start_experiment(cfg: dict) -> logging.Logger:
    logger = get_logger(cfg)

    fix_seed(41)

    logger = get_git_info(logger)
    logger = get_os_info(logger)

    return logger
victor-von-pooh commented 6 months ago

@tottocoslowlifer ご対応ありがとうございます.

fix_seed(41)

こちらのみ引数を config の "seed" で読み取れるようにしてください. それ以外は OK なので tide/experiment_tools/set_up.py に入れて commit してください.

tottocoslowlifer commented 6 months ago

ありがとうございます. 関数get_logger()およびfix_seed()は他のファイルで定義されている状態ですが,このままコミットして大丈夫でしょうか.

victor-von-pooh commented 6 months ago

@tottocoslowlifer

関数get_logger()およびfix_seed()は他のファイルで定義されている状態ですが,このままコミットして大丈夫でしょうか.

このファイルで使えるように適切に追記してください.

tottocoslowlifer commented 6 months ago

かしこまりました.コミットが完了いたしました.