喫茶's CAFE

心にうつりゆくよしなしコードをそこはかとなく

技術系はZennに書くことにしました。

先日、以下のような記事を書いた矢先なのですが、技術系の記事はZennに書くことにしました。

hackkisser.hatenablog.com

なぜ、はてなブログではなくZennなのかというと私が最近何かを書き留める時にObsidianを使っていることが大きな理由です。

 

Obsidianで私が記事を書くやり方ですが、まず最初にChatGPT、Claude、Geminiあたりにさくっと出力してもらっています。その手順に沿って試したり、調べたり時には自分の知識を使いながら加筆、修正しています。ほとんど書き直すときもあるんですが、何か道しるべがあるとないとでは書きやすさが全然違いますし、なにより始める労力が小さいことが私にとっては非常に大きいです。

 

さて、Obsidianはマークダウンが使えてコードもコードブロックを使って書けるのですが、私はこのコードブロックの見た目にCode-Stylerというプラグインを利用しています。

github.com

このプラグインを使うとコードブロックがとてもリッチになるのですが、特に私はコードの変更箇所がわかる機能がお気に入りです。以下のコードもこれがあるとどこが追加したところなのか一目でわかります。(他にも言語のアイコンを表示したりする機能があったりしますが私は使っていません)

Obsidianに書いたコード箇所

この機能なのですが残念ながらはてなブログには標準としてないようでした。一方でZennには少し使い方が違うもののこれを実現する機能がありました。

Zennに書いた同じコード箇所

 

このことから記事を書く際にObsidianからそのままコピーするスタイルの私にとってはZennの方が親和性を高く感じました。Zennの方が読む人にもわかりやすく書けるなと思ったため、今後はしばらくZennの方に書こうかと思います。

 

はてなブログは日々思ったことを書く場所になりそうです。

 

といいつつそのうちまた気が変わるかもしれません。

 

Zennもよろしくお願い致します。 ↓

zenn.dev

 

 

 

自尊心と羞恥心を捨て虎から人に成る

その声は、我が友、李徴子ではないか?

山月記という物語がある。日本では高校時代に教わる物語のため多くの人が知っているのではないでしょうか。

この物語には2人の登場人物がいます。
袁傪と李徴です。

観察御史(地方の不正を取り締まる役)である袁傪が、仕事先に向かう途中で虎となった旧友の李徴と出会い、虎になった経緯やその心の内を聞くというのが話の要旨です。

物語の中で李徴が虎になった理由は「臆病な自尊心」と「尊大な羞恥心」にあると語られています。

李徴は自分が優れた詩の才能を持ち、いつか詩人として成功すると強く自負しながら、一方で自身の才能がそれほどでもないことを知るのを恐れて他人の中に入って切磋琢磨することを嫌いました。
そうして1人こもるうちに、その自尊心はどんどん肥大化していき、現実に向き合うこともできず傲慢な態度で周りの人を傷つけるようになり、その心の持ちようを李徴は虎に形容しています。

更にはその心持が外見さえも虎に変えてしまったと語っています。

さて現代では自己愛性パーソナリティー障害と名前が付きそうな李徴の心のうちですが、最近これを自分のこととしてとらえる機会がありました。

このブログは今までネットの記事に助けてもらった感謝に報いるために自分も誰かの役に立つ情報を発信しようという想いから始めました。

しかしながら誰かの役に立つ情報を発信したいという想いが、他の人が書いていることを書くまでもないという自尊心と、それほど詳しくもない自分が発信してもいいんだろうかという羞恥心の末に8年間で書いた記事は2つだけという惨状です。

私自身も李徴と同じく優れたエンジニアになりたい自分と、そうでない自分の中の葛藤の末に、世の中のそういったコミュニティに積極的に入っていくことも自ら積極的にアウトプットをすることもしないでしまっていました。そしてXなどで流れてくるポストを見ては難しい専門記事を理解できる自分に自尊心を満たし、知識の甘い記事を見ると勝手に批評のポストを流したりしていたのです。

まさしく、私自身も虎になっていたのだと思います。

李徴はもはや手遅れと虎になった自らを肯定して草むらの中へ消えていきましたが、私はそうしたいとは思いません。

優れたエンジニアもしくは普通のエンジニアにさえなるには手遅れかもしれませんが、そこに繋がる道を歩きたいとは思います。ということでこれからできるだけこのブログを更新していくつもりです。あまり記事の質は高くないかもしれませんが、そもそも東北の場末のエンジニアの記事を誰が読むというのでしょうか?(いや読むまい)

誰かが読むのではなんて自尊心は捨て、そもそも読まれないのだから羞恥心など捨て去ります。

この投稿をもって私は虎ではなく、ただの人への1歩を進もうと思います。

Visual Studio CodeのgitでincludeIfがうまくいかなかった話

今まではMacで開発することが多かったのですが、良いマシンを買ったので開発環境をWindowsに作りました。 githubだけでなくgitbucketを使うこともあるので、それぞれのプロジェクトでgitの設定を変えて使っています。 ただ各レポジトリの.gitconfigを編集するのはとても面倒くさい。

そこでMacのときはグローバルの.gitconfigをgithub用の設定に、gitbucket用の設定は対象のディレクトリ以下のレポジトリに対する設定をincludeIfで読み込むようにしていました。 以下のファイルのような設定をすると~/work以下のレポジトリには.gitconfig-work の設定が適用されます。 Visual Studio Code上でgitを操作していても期待したとおりの動きをしてくれていました。

~/.gitconfig

[user] 
    user = user_example
    email = hoge@example.jp

 [includeIf "gitdir:~/work/"] 
    path = ~/.gitconfig-work 

~/.gitconfig-work

[user] 
    user = user_work 
    email = hoge@work.com 

ところがwindowsで同じことをしたところ、なぜかVisual Studio Code上でgitを操作したときだけ.gitconfig-workの設定を読んでくれませんでした。 さすがWindowsすんなりと通してはくれません。ただ割とすぐに解決方法は見つかりました。

Git integration does not correctly support `includeIf` directive · Issue #40354 · microsoft/vscode · GitHub

さきほどの.gitconfigでincludeIfの部分を以下の用に変えるだけです。 Visual Studio Codeでgitを操作しているときにパスの大文字小文字の区別がなくなり、対象のディレクトリパスをちゃんと読んでくれなかったみたいです。 gitdir/iにするとその後ろの対象ディレクトリを大文字、小文字の区別なく認識してくれるとのこと。

~/.gitconfig

[user] 
    user = user_example 
    email = hoge@example.jp 

[includeIf "gitdir/i:~/work/"] 
    path = ~/.gitconfig-work 

とりあえずGridSearchCVを使ってみたい

最初にLGBMを使って回帰モデルを作る

まずは簡単に回帰モデルを作ってみます。使うデータはscikit-leanの中にあるBostonデータセットになります。これは米国ボストン市郊外における地域別のデータで、実際にどんなデータか見てみます。

import numpy as np
from sklearn.datasets import load_boston
import pandas as pd

boston = load_boston()
train_df = pd.DataFrame(boston.data, columns = boston.feature_names)
train_df['HousePriceMedian'] = boston.target
train_df.head()
CRIM ZN INDUS CHAS NOX ・・・ HousePriceMedian
0 0.00632 18.0 2.31 0.0 0.538 ・・・ 24.0
1 0.02731 0.0 7.07 0.0 0.469 ・・・ 21.6
2 0.02729 0.0 7.07 0.0 0.469 ・・・ 34.7
3 0.03237 0.0 2.18 0.0 0.458 ・・・ 33.4
4 0.06905 0.0 2.18 0.0 0.458 ・・・ 36.2

左から13列のデータ(地域のいろいろな特徴)を使って一番右のデータ(住宅価格の中央値)を予測するのが今回のミッションです。

その前に後々検証に使うのでtrain_test_split()を用いてデータを分割します。train_dfを訓練用データ,val_dfをモデルの評価用データとして使います。

from sklearn.model_selection import train_test_split
train_df, val_df = train_test_split(train_df, test_size=.10, random_state=1)

本当であればデータの欠損値を埋めたり、目的変数の分布や外れ値をどうするかなどの前処理をしないといけないのですが、今回はそれなりに前処理されているようなのでそのままモデルの構築に進みます。回帰するにあたってはkaggleでも人気の高いLightGBMを使います。誤差にはRoot Mean Square Errorを指定!

import lightgbm as lgb

target = 'HousePriceMedian' #目的変数
predictors =['CRIM','ZN','INDUS','CHAS','NOX','RM','AGE','DIS','RAD','TAX','PTRATIO','B','LSTAT'] #説明変数

#モデルの作成、各種ハイパラメータの指定
bst = lgb.LGBMRegressor(
                        num_leaves = 31,
                        learning_rate=0.01,
                        min_child_samples=10,
                        n_estimators=1000,
                        max_depth=-1,
                        )

#トレーニング開始
bst.fit(
        train_df[predictors], 
        train_df[target],
        eval_names=['train'],
        eval_set=[(train_df[predictors].values,train_df[target].values)],
        eval_metric='rmse',
        early_stopping_rounds=10,
        verbose = 0
       )

from sklearn.metrics import mean_squared_error
pred1 = bst.predict(val_df[predictors])
RMSE1 = np.sqrt(mean_squared_error(pred1, val_df[target]))
print('default RMSE:{}'.format(RMSE1))
default RMSE:3.216731430057823

ものすごく悪い値が出た!?まあ何も前処理してないしそんなものかなあ、、、、、 交差検証をするだけならcross_val_score()を使うと良いみたいです。cvにはただの整数を入れれば良いのですが、それだと前から順番に分割されるので分割の際にシャッフルしたい時はcvにKFold()のshuffle=Trueにして渡してやります。cros_val_scoreはscoringで指定した誤差の値を返すのですが何故か負のMean Square Errorを返すので符号を反転します。

from sklearn.model_selection import cross_val_score,KFold

scores = -cross_val_score(bst, train_df[predictors], 
                          train_df[target],
                          scoring = 'neg_mean_squared_error',
                          cv = KFold(n_splits=3, shuffle=True)
                         )
scores = np.sqrt(scores)
# 各分割におけるスコア
print('Cross-Validation scores: {}'.format(scores))
# スコアの平均値
print('Average score: {}'.format(np.mean(scores)))
Cross-Validation scores: [3.4143698  4.64896828 3.61283182]
Average score: 3.892056630769702

スコアに関してはもう何も考えない、、、、、、、、

ここからが本番

ようやくGridSearchCV()を使って見ます。GridSearchCV()には以下のようにハイパラメータの試したい値をリストにして、さらにそれらを辞書にして渡してやります。

grid_param ={'n_estimators':[1000,2000],'max_depth':[4,8,16],'num_leaves':[31,15,7,3],'learning_rate':[0.1,0.05,0.01]}

またearly_stoppingを使いたいので以下のような辞書をモデルを訓練する際に渡してやります。

fit_params={'early_stopping_rounds':10, 
            'eval_metric' : 'rmse', 
            'eval_set' : [(train_df[predictors], train_df[target])]
           }

では実際にGridSearchCV()を使って見ましょう。

from sklearn.model_selection import GridSearchCV



bst_gs_cv = GridSearchCV(
            bst, # 識別器
            grid_param, # 最適化したいパラメータセット 
            cv = KFold(n_splits=3, shuffle=True), # 交差検定の回数
            scoring = 'neg_mean_squared_error',
            verbose = 0
            )

bst_gs_cv.fit(
            train_df[predictors], 
            train_df[target],
            **fit_params,
            verbose = 0
            )

best_param = bst_gs_cv.best_params_
print('Best parameter: {}'.format(best_param))

pred2 = bst_gs_cv.predict(val_df[predictors])
RMSE2 = np.sqrt(mean_squared_error(pred2, val_df[target]))
print('GridSearchCV RMSE:{}'.format(RMSE2))
Best parameter: {'max_depth': 4, 'n_estimators': 1000, 'num_leaves': 7, 'learning_rate': 0.05}
GridSearchCV RMSE:2.938220045864752

誤差減ったからよしとしよう

反省

もうちょっと内容しぼってブログ書きます。