驚くほど簡単な技術系健忘録

驚くほど簡単な技術系健忘録

アプリやWebサービス、RPAを作る上での健忘録を書いていきます。

Optunaでパラメータチューニングしてみた【第一弾】

rin-effort.com

このサイトを参考にOptunaでパラメータチューニングを初めてやってみた。
参考サイトは別のCSVのデータで行っていますが、こちらの記事ではsklearnに入っているデータを利用してみた。

参考サイトですごいなと思ったのは他のサイトであるあるな感じでoptunaで普通に単純にパラメータを出すだけではなく、rmse値を最小化するようにパラメータをチューニングする以下の関数

def objective(trial):
    params = {'metric': {'rmse'},
              'max_depth' : trial.suggest_int('max_depth', 1, 10),
              'subsumple' : trial.suggest_uniform('subsumple', 0.0, 1.0),
              'subsample_freq' : trial.suggest_int('subsample_freq', 0, 1),
              'leaning_rate' : trial.suggest_loguniform('leaning_rate', 1e-5, 1),
              'feature_fraction' : trial.suggest_uniform('feature_fraction', 0.0, 1.0),
              'lambda_l1' : trial.suggest_uniform('lambda_l1' , 0.0, 1.0),
              'lambda_l2' : trial.suggest_uniform('lambda_l2' , 0.0, 1.0)}
 
    gbm = lgb.train(params,
                    lgb_train,
                    valid_sets=(lgb_train, lgb_eval),
                    num_boost_round=10000,
                    early_stopping_rounds=100,
                    verbose_eval=50)
    predicted = gbm.predict(X_test)
    RMSE = np.sqrt(mean_squared_error(y_test, predicted))
    
    pruning_callback = optuna.integration.LightGBMPruningCallback(trial, 'rmse')
    return RMSE

を作っていたのが非常に面白かったので利用してみました。

以下がコードの全貌です。

#データセット用のライブラリ
from sklearn import datasets, model_selection

#データ分析用のライブラリ
import pandas as pd
import numpy as np

#データ可視化ライブラリ
import matplotlib.pyplot as plt
import seaborn as sns

#LightGBMライブラリ
import lightgbm as lgb

#訓練データとモデル評価用データに分けるライブラリ
from sklearn.model_selection import train_test_split

#ハイパーパラメータチューニング自動化ライブラリ
import optuna

#関数処理で必要なライブラリ
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score

#その他
from sklearn import metrics

#データ準備
features, labels = datasets.load_boston(return_X_y = True)
#データ分割
X_train,X_test,y_train,y_test = train_test_split(features, labels, test_size=0.2)

#LightGBM用のデータセットに加工
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test)

#optunaへ入れる情報をまとめた関数
def objective(trial):
    params = {'metric': {'rmse'},
              'max_depth' : trial.suggest_int('max_depth', 1, 10),
              'subsumple' : trial.suggest_uniform('subsumple', 0.0, 1.0),
              'subsample_freq' : trial.suggest_int('subsample_freq', 0, 1),
              'leaning_rate' : trial.suggest_loguniform('leaning_rate', 1e-5, 1),
              'feature_fraction' : trial.suggest_uniform('feature_fraction', 0.0, 1.0),
              'lambda_l1' : trial.suggest_uniform('lambda_l1' , 0.0, 1.0),
              'lambda_l2' : trial.suggest_uniform('lambda_l2' , 0.0, 1.0)}
 
    gbm = lgb.train(params,
                    lgb_train,
                    valid_sets=(lgb_train, lgb_eval),
                    num_boost_round=1000,
                    early_stopping_rounds=100,
                    verbose_eval=300)
    predicted = gbm.predict(X_test)
    RMSE = np.sqrt(mean_squared_error(y_test, predicted))
    
    pruning_callback = optuna.integration.LightGBMPruningCallback(trial, 'rmse')
    return RMSE

#optunaの起動
study = optuna.create_study()
study.optimize(objective, timeout = 60)

print('Best trial:')
trial = study.best_trial
print('Value:{}'.format(trial.value))
print('Paramas:')
for key, value in trial.params.items():
    print(',"{}":{}'.format(key, value))

#予測値と正解値を描写する関数
def True_Pred_map(pred_df):
    RMSE = np.sqrt(mean_squared_error(pred_df['true'], pred_df['pred']))
    R2 = r2_score(pred_df['true'], pred_df['pred']) 
    plt.figure(figsize=(8,8))
    ax = plt.subplot(111)
    ax.scatter('true', 'pred', data=pred_df)
    ax.set_xlabel('True Value', fontsize=15)
    ax.set_ylabel('Pred Value', fontsize=15)
    ax.set_xlim(pred_df.min().min()-0.1 , pred_df.max().max()+0.1)
    ax.set_ylim(pred_df.min().min()-0.1 , pred_df.max().max()+0.1)
    x = np.linspace(pred_df.min().min()-0.1, pred_df.max().max()+0.1, 2)
    y = x
    ax.plot(x,y,'r-')
    plt.text(0.1, 0.9, 'RMSE = {}'.format(str(round(RMSE, 5))), transform=ax.transAxes, fontsize=15)
    plt.text(0.1, 0.8, 'R^2 = {}'.format(str(round(R2, 5))), transform=ax.transAxes, fontsize=15)

#Optunaで最適化されたパラメータ
params = {
    "metric": {'rmse'}
    ,"max_depth":4
    ,"subsumple":0.5654007940457206
    ,"subsample_freq":0
    ,"leaning_rate":0.00017894703178220227
    ,"feature_fraction":0.5230674363641012
    ,"lambda_l1":0.7312452385011556
    ,"lambda_l2":0.021966875171314314
}

#LightGBMのモデル構築
model = lgb.train(
    params
    ,lgb_train
    ,valid_sets=(lgb_train, lgb_eval)
    ,num_boost_round=10000
    ,early_stopping_rounds=100
    ,verbose_eval=300
)

#モデル評価用データで予測値出力
y_pred = model.predict(X_test)

#可視化関数にぶち込めるように予測値と正答値をデータフレームに加工
pred_df = pd.concat([pd.Series(y_test), pd.Series(y_pred)], axis=1)
pred_df.columns = ['true', 'pred']

#可視化関数を実行
True_Pred_map(pred_df)

# 特徴量の重要度をプロット
lgb.plot_importance(model)


何もしていないのに結構精度が良くてさすがLightGBMとOptunaですね。
f:id:nade_nadegata:20210607001724p:plain

InstagramのAPIをGASを使って叩いてみた

参考の記事はこちら
qiita.com

InstagramAPIに関するアクセストークンやIDは簡単に取得できず、FacebookAPIから何度かAIPを叩いてようやく取得というのが本当にめんどくさい。

以下コードを転載します。

function reporting(){
  var today = new Date();

  //instagram数値記録用のスプレットシートID
  var SSId = '【スプレッドシートID】';

  //instagram Graph API 必要情報
  var instragramID = '【InstagramのビジネスアカウントID】';
  var username = '【Instagramユーザー名】';
  var ACCESS_TOKEN = "【アクセストークンC】";
  getFollower(today,SSId,instragramID,username,ACCESS_TOKEN);
}

//instagramの数値を引っ張り記録する関数
function getFollower(date,SSId,instragramID,username,ACCESS_TOKEN) {

  var mySS = SpreadsheetApp.openById(SSId); //IDでスプレッドシートを開く
  var sheetName = username; //シートの名前をインスタユーザー名
  var sheet = mySS.getSheetByName(sheetName);

  //日付フォーマット
  var today = Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy/MM/dd');
  //Graph API followers_count(フォロワー数),follows_count(フォロー数),media_count(投稿数)を取りに行く
  var facebook_url = 'https://graph.facebook.com/v4.0/'+ instragramID +'?fields=business_discovery.username('+ username +'){followers_count,follows_count,media_count}&access_token='+ ACCESS_TOKEN;

  var encodedURI = encodeURI(facebook_url);
  var response = UrlFetchApp.fetch(encodedURI); //URLから情報を取得
  var jsonData = JSON.parse(response);//JSONデータをパース
  var followers = jsonData['business_discovery']['followers_count'];
  var follows = jsonData['business_discovery']['follows_count'];
  var media_count = jsonData['business_discovery']['media_count'];

  //シートにデータを追加またはアップデート
  var newData =[today,followers,follows,media_count];
  insertOrUpdate(sheet, newData);
}

//行の存在に応じて追加もしくは更新を行う関数
function insertOrUpdate(sheet, data) {
  var row = findRow(sheet, data[0]);//日付比較の関数、行番号を受け取る
  if (row) { // 行が見つかったら更新
    sheet.getRange(row, 1, 1, data.length).setValues([data]);
  } else { // 行が見つからなかったら新しくデータを挿入
    sheet.appendRow(data);
  }
}

// 日付比較を行い、データがあれば行番号を返す関数
function findRow(sheet, date) {
  var searchDate = Utilities.formatDate(new Date(date), 'Asia/Tokyo','yyyy/MM/dd');
  var values = sheet.getDataRange().getValues();
  Logger.log(values + "findRow");
  for (var i = values.length - 1; i > 0; i--) {
    var dataDate = Utilities.formatDate(new Date(values[i][0]), 'Asia/Tokyo','yyyy/MM/dd');
    if (dataDate == searchDate) {
      return i + 1;
    }
  }
  return false;
}

SQLインジェクション脆弱性を持つサイトを作ってみた

dev.classmethod.jp
ここのサイトを真似してSQLインジェクション脆弱性を持つサイトを作ろうとしたら色々とハマったので覚書。
※そもそもPHPでデータベースに接続することすらしたことない素人が頑張ってみました。

まずはログイン画面(これは上のサイトのソースをそのままコピペ)

■index.html
<html>
  <meta charset="UTF-8">
  <head>
    <title>ログイン画面</title>
  </head>
  <body>
    <form action="db.php" method="post">
      <table>
        <tr>
          <td>ユーザID</td>
          <td><input type="text" name="uid"></td>
        </tr>
        <tr>
          <td>パスワード</td>
          <td><input type="text" name="password"></td>
        </tr>
      </table>
      <input type="submit" value="ログイン">
    </form>
  </body>
</html>

次にデータベース接続を行ったりSELECT文を実行するページこれがこのページの最初に書いたブログでは情報が古くてそのままコピペできなかった。
ということでこちらは以下のQiitaの記事等を色々と参照。
■単純な接続部分
qiita.com
■接続確認コード含む等
kahoo.blog

■db.php
<html>
  <meta charset="UTF-8">
  <head>
    <title>ログイン処理</title>
  </head>
  <body>
<?php
// エラーを出力する
ini_set('display_errors', "On");

// defineの値は環境によって変えてください。
define('HOSTNAME', 'localhost');
define('DATABASE', 'system');
define('USERNAME', 'root');
define('PASSWORD', 'root');

//入力されたIDとpassword
$uid = $_POST['uid'];
$pass = $_POST['password'];

//データベース接続
$db  = new PDO('mysql:host=' . HOSTNAME . ';dbname=' . DATABASE, USERNAME, PASSWORD);

//SQL実行と結果
$sql = "SELECT email FROM users where uid='$uid' AND passwd='{$pass}'";
$stmt = $db->query($sql);
$stmt->execute();
$count=$stmt->rowCount();

//入力に誤りがあった場合
if ($count == 0) {
 echo "ユーザ名または、パスワードに誤りがあります。";
 exit;
}

//SQLで取得したデータを表示
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
  print('email addressはこちらです '.$result['email']);
  print('<br>');
}

$dbh = null;

?>
  <a href="index.html">ログイン画面に戻る</a>
  </body>
</html>

なお、IDやパスワードが誤っていた場合は取得した結果が0行ということで判別しているが、PDOで行数(レコード数)をカウントする関数がなくてややこしかった。それくらい用意してくれよ。このブログを参考にして解決できたから良いけど。
www.php-mysql-linux.com

■データベース

systemというデータベースにusersテーブルを作成する。テーブルの中身はこんな感じ。
usersテーブルを作成します。

uid varchar(20)
passwd varchar(20)
email varchar(20)

レコードは2レコード以上入れておきましょう。

SQLインジェクションを試してみる

パスワード欄に「'OR 'A' = 'A」と入れると全ユーザのデータが表示されて情報漏洩が起こることが確認できると思います。

Djangoを触ってみた

Pythonを最近勉強しているのでその流れでなにかWrbサービスが作れないかと思ってDjangoを触ってみた。
note.com
このサイトを真似して作って見てわかったけど、正直何も理解できない(笑)

Webってなんでこんなにファイル構造が複雑でファイルが多いんだ。
ちょっと引続き勉強しよう。もしくは諦めるか。

LightGBMのサンプルはどれが一番良いか

個人的にはここのサンプルが最も理解しやすかったので忘れないように書いておく
mathmatical22.xyz


あとはこの記事が初めてLightGBMを触った際に参考にさせてもらったブログということでこちらも記載
qiita.com


こういうサンプルを上げられる人ってすごいな〜。
日々精進しないと。

pandasで条件に合わせてデータを書き換える

■lambdaを利用する方法

#0,1でフラグを立てる時には便利
#columnsの値が'ok'の場合colums1を1にしてそれ以外の場合には0にする
df['column1'] = df['column2'].apply(lambda x: 1 if x == 'ok' else 0)


■maskを利用する方法

#条件を満たす場合のみ値を変更
#columnsの値が'ok'の場合colums1を1にしてそれ以外の場合にはそのまま
df['column1'] = df['column1'].mask(df['column2'] == 'ok', 1)


■whereを利用する方法

#maskの反対で条件を満たさない場合にのみ値を変更
#columnsの値が'ok'の場合colums1はそのままにして'ok'ではない場合に1にする
df['column1'] = df['column1'].where(df['column2'] == 'ok', 1)


参考の記事はこちら
note.nkmk.me


なんだかSQLに慣れているとこのコードが結構違和感。

MacのAnacondaにlibompをインストールするのは不可

Anacondaに

conda install lightgbm -c conda-forge

でLightGBMをインストールして機械学習を試そうと思ったらlibompがインストールされていません的なワーニングが出た。
実際に実行するのには問題はないがなんか気持ち悪いので

conda install libomp -c conda-forge

とやってみたけどインストールはできない。
そこでGoogleで「libomp anaconda」と調べると
anaconda.org
これが検索結果の一番上に出たのでとりあえず

conda install -c conda-forge openmp

と打ってインストールしてみたらうまく行った。

ということでMacのAnacondaでLightGBMを使うときには「openmp」も入れたほうが良さそうだよという話でした。

理由はわかっていません。