Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

React 画面遷移方法

前回の記事にてReactでの画面遷移方法は後回しにしました。
elsammit-beginnerblg.hatenablog.com


今回は後回しにした画面遷移についてまとめたいと思います!!



■画面遷移環境構築

Reactでの画面遷移を行うにあたり、
「react-router-dom」
というライブラリを用います。

react-router-domに似たreact-routerがありますが、こちらとの差異はこちらにまとまっております。
https://qiita.com/koja1234/items/486f7396ed9c2568b235


react-router-domライブラリについてはこちらに掲載されています。
GitHub - ReactTraining/react-router: Declarative routing for Reacthttps://github.com/ReactTraining/react-router/tree/master/packages/react-router-dom

インストール方法はこちらになります。

npm install react-router-dom

or

yarn add install react-router-dom

■画面遷移方法

今回は前回作成したGoogleトレンドでの検索画面の検索結果画面を例にして進めていきたいと思います!!
画面遷移の流れですが、
 検索画面⇒検索結果画面
の順で遷移させていきたいと思います。

まずは、検索画面、検索結果画面をそれぞれ作成していきます。<検索画面>

  InputForm = () => {
    return(
      <div>
        <h1> Googleトレンド検索画面</h1>
        <input type="text" id="SearchName" maxLength="50"/><br/>
        <button>
            <Link to='/about' onClick={this.doSomething}>確定</Link>
        </button>
      </div>
    );
  }

<検索結果画面>

  ResultForm = () =>{
    const {Search} = this.state;
    return (
      <div>
        <h1> Google検索結果</h1>
        <div id="widget">
          <GoogleTrends
            type="TIMESERIES"
            keyword={Search}
            url="https://ssl.gstatic.com/trends_nrtr/2051_RC11/embed_loader.js"
          /> 
          <button>
            <Link to='/'>戻る</Link>
          </button>
        </div>
      </div>
    );
  }

ここで、

<Link to='/about' onClick={this.doSomething}>確定</Link>

<Link to='/'>戻る</Link>

がそれぞれページの遷移先を示しております。

to='/' や to='/about'が遷移先のパスに該当します。
今回のソースでは、bottunタグで囲んでいるため、ボタン押下時にLinkが読み出されて遷移先パスへの画面遷移が実行されます。

次にページ遷移させるためのパス定義ですがこちらになります。

      <BrowserRouter>
        <div>
          <Route path='/about' component={this.ResultForm} /> 
          <Route exact path='/' component={this.InputForm} />
        </div>
      </BrowserRouter>

BrowserRouterタグ内にRouteタグでpath='~~'といった形でパスを定義します。
定義したパスにアクセスした場合の実行処理をcomponentに記載します。
今回は、
 ・検索画面:パス:/ 実行処理:this.ResultForm
 ・検索結果画面:パス:/about 実行処理:this.InputForm
となります。

こちらの定義を、

  render(){
    return (<div>
      <BrowserRouter>
        <div>
          <Route path='/about' component={this.ResultForm} /> 
          <Route exact path='/' component={this.InputForm} />
        </div>
      </BrowserRouter>
    </div>);
    }

といったように、render()の中に定義します。

これで'/'にアクセスすると検索画面が表示され、
確定ボタンを押下すると'/about'パスに画面が遷移し、検索結果画面が表示されます。

今回は関数でしたが、クラス毎に画面を用意してた上で遷移させることも可能です。
この場合にはこちらのようになります。<検索画面>

// pageone.js

import React from "react";
import {Link } from 'react-router-dom'
export default class pageone extends React.Component  {
    render(){
        return (<div>
            <h1> 検索画面</h1>
            <button>
                <Link to='/about'>確定</Link>
            </button>
          </div>);
        }
}

<検索結果>

// pagetwo.js

import React from "react";
import { Link } from 'react-router-dom'
export default class pagetwo extends React.Component  {
    render(){
        return (<div>
            <h1> 検索結果画面</h1>
            <button>
                <Link to='/'>確定</Link>
            </button>
          </div>);
        }
}

<ページ遷移定義>

//App.js

import { BrowserRouter, Route } from 'react-router-dom'
import Pageone from './pageone';
import Pagetwo from './pagetwo';
function App() {
  return (
    <div className="App">
        <BrowserRouter>
        <div>
          <Route path='/about' component={Pagetwo} /> 
          <Route exact path='/' component={Pageone} />
        </div>
      </BrowserRouter>
    </div>
  );
}

■最後に

react-router-domライブラリを使用すると簡単にページ遷移できることがわかりました!!
今まで単一画面しか作ってこなかったからこれからはページ遷移も使っていきたいと思います!!

後、結構ライブラリ関係はGithubで管理されているな。と感じました。
Githubも使えるようになっていきたいと感じた今日この頃。

わかばちゃんと学ぶ Git使い方入門

新品価格
¥2,208から
(2020/10/4 22:51時点)


ReactでGoogleトレンドを挿入してみる

今回はReactにてページにGoogleトレンドを挿入方法についてまとめたいと思います!!
単純には、Googleトレンド自体をページ内に表示すればそれで済む気もしますが、カスタマイズ性は自分で作ったほうが高いので挑戦しました!!

Googleトレンドとは?

特定の期間内でどのようなキーワードが多く検索されているかをチェックすることができるツールです。
検索では、文字以外にも画像やニュース、yutube検索などが行えます。

本ツールを用いることで検索数の動向から現在のトレンドをいち早く知ることができるツールとなります。

■ReactへのGoogleトレンド挿入

pythonだとpytrendsというGoogleトレンド用のAPIが存在するのですが、Reactだとどうなんだろう??
調べてみた結果、APIが用意されておりました。
APIというよりウェジェットですね。

stack overflowにてquestion & answerがなされおり、
そちらにサンプルも載っておりましたので参考にさせて頂くことに。
reactjs - How to Embed Google Trends into React app - Stack Overflow


利用するファイルはApp.jsとGoogleTrend.jsであり、それぞれのコードはこちらになります。

// App.js

import React from "react";
import GoogleTrends from "./GoogleTrends";

export default function App() {
  return (
    <>
      <div id="widget">
        <GoogleTrends
          type="TIMESERIES"
          keyword="Celine Dion"
          url="https://ssl.gstatic.com/trends_nrtr/2051_RC11/embed_loader.js"
        />
      </div>
    </>
  );
}
// GoogleTrends.js

import React from "react";
import ReactDOM from "react-dom";
import Script from "react-load-script";

export default function GoogleTrends({ type, keyword, url }) {
  const handleScriptLoad = _ => {
    window.trends.embed.renderExploreWidgetTo(
      document.getElementById("widget"),
      type,
      {
        comparisonItem: [{ keyword, geo: "US", time: "today 12-m" }],
        category: 0,
        property: ""
      },
      {
        exploreQuery: `q=${encodeURI(keyword)}&geo=US&date=today 12-m`,
        guestPath: "https://trends.google.com:443/trends/embed/"
      }
    );
  };

  const renderGoogleTrend = _ => {
    return <Script url={url} onLoad={handleScriptLoad} />;
  };

  return <div className="googleTrend">{renderGoogleTrend()}</div>;
}

まずGoogleTrends.jsから。
Gooleトレンドでの検索結果表示ウェジェットのコードはこちらになります。

  const handleScriptLoad = _ => {
    window.trends.embed.renderExploreWidgetTo(
      document.getElementById("widget"),
      type,
      {
        comparisonItem: [{ keyword, geo: "US", time: "today 12-m" }],
        category: 0,
        property: ""
      },
      {
        exploreQuery: `q=${encodeURI(keyword)}&geo=US&date=today 12-m`,
        guestPath: "https://trends.google.com:443/trends/embed/"
      }
    );
  };

"type"には表示するグラフ、keywordには検索ワードが代入されます。
また、"geo"にて検索対象国を設定します。
コードはUSですが、JPにすることで日本国内指定が可能になります。

こちらのウェジェットを、

  const renderGoogleTrend = _ => {
    return <Script url={url} onLoad={handleScriptLoad} />;
  };

により画面描画時に表示するようなコードになっています。

このGooleトレンドのウェジェットをページに挿入(埋め込む)ためのコードが、
App.jsのこちらにあたります。

      <div id="widget">
        <GoogleTrends
          type="TIMESERIES"
          keyword="Celine Dion"
          url="https://ssl.gstatic.com/trends_nrtr/2051_RC11/embed_loader.js"
        />

GoogleTrendsタグで、typeとkeywordとurlを指定する必要があります。
typeとkyewordは先ほど記載した通り、
"type"には表示するグラフ、keywordには検索ワード
が該当します。
urlは基本的にはこちらのソース流用で問題ないです。

こちらを実行すると確かにkeywordに指定した検索ワードに対するGoogleトレンド結果が表示されました。

■サンプルコード改良

サンプルだと検索ワードをソースに埋め込まないといけないため、自由にワード入力・検索ができないです。
そこで、ワード入力によりGoogleトレンド結果が表示できるようにコードの修正を試みました!!

が、、、どうもにも簡単ではない😢
どうにも、

  const handleScriptLoad = _ => {
    window.trends.embed.renderExploreWidgetTo(

の箇所で、

<Script url={url} onLoad={handleScriptLoad} />

のようにonLoadでコールした場合には有効になるのだが、それ以外のパターンでは

window.trends.embed.renderExploreWidgetTo

がこんな感じでundefinedになってしまう。。。

Uncaught TypeError: Cannot read property 'embed' of undefined

複数回連続でアクセスされると負荷がかかるから初回時のみ有効になる仕様にしているのかな?
window.trends.embedはGoogleトレンドが提供している部分だからこちらからだとどうしようもない😨


ということで、苦肉の策!!
検索画面と結果表示画面を分けることにしました!!

画面遷移方法については別ブログにまとめるとして、結果はこんな感じになりました!!
f:id:Elsammit:20201003145048g:plain



コードはこちら。

//App.js

import React from "react";
import GoogleTrends from "./GoogleTrends";
import "./style.css";
import { BrowserRouter, Route, Link } from 'react-router-dom'

export default class Google extends React.Component  {
  constructor (props) {
    super(props);
    this.state = {
        Search:"",
        type:"TIMESERIES",
        url:"https://ssl.gstatic.com/trends_nrtr/2051_RC11/embed_loader.js",
    };
  }

  doSomething = () =>{
    var ElementName = "SearchName";
    console.log(ElementName)
    var input = document.getElementById(ElementName).value;
    
    this.setState({
      Search:input
    })
  }

  InputForm = () => {
    return(
      <div>
        <h1> Googleトレンド検索画面</h1>
        <input type="text" id="SearchName" maxLength="50"/><br/>
        <button>
            <Link to='/about' onClick={this.doSomething}>確定</Link>
        </button>
      </div>
    );
  }

  ResultForm = () =>{
    const {Search} = this.state;
    return (
      <div>
        <h1> Google検索結果</h1>
        <div id="widget">
          <GoogleTrends
            type="TIMESERIES"
            keyword={Search}
            url="https://ssl.gstatic.com/trends_nrtr/2051_RC11/embed_loader.js"
          /> 
          <button>
            <Link to='/'>戻る</Link>
          </button>
        </div>
      </div>
    );
  }

  render(){
    return (<div>
      <BrowserRouter>
        <div>
          <Route path='/about' component={this.ResultForm} /> 
          <Route exact path='/' component={this.InputForm} />
        </div>
      </BrowserRouter>
    </div>);
    }
  }

■最後に

今回自前でGoogleTrends.jsを用意しましたが、APIを公開している方もいらっしゃいました。
https://github.com/null-none/widget-google-trends

ただ、こちらを利用すると
syntaxErrorが発生してしまい、うまく利用できませんでした。
GitHubのIssueにも上がっている問題でOpen状態なので現在修正中なのかな??それともドロップかな??
このようにAPIとして提供いただけると利用側はとても便利ですね。

後、せっかくGoogleトレンドがページに挿入できるようになったので何かいいアプリ作れたらいいな😊

knnを用いた多クラス分類でのパラメータ調整

前回、knn(k-近傍法)で多クラス分類を行いました。
結果は散々😢
上手く分類出来ていない結果となり悲しい泣
elsammit-beginnerblg.hatenablog.com


ということで、今回はknnに対してパラメータの調整を行いながら最適解を見つけていきたいと思います!!

■前回の結果

前回は、こちらのパラメータで実施!!
 ・k値:8
 ・トレーニング、テストの分割:50%


その結果はこちら。。。残念な結果だ。。。😢😢

合計ポケモン数:92匹
正しくでんきタイプと判断したポケモンの数: 23匹
正しくはがねタイプと判断したポケモンの数: 22匹
正しくでんきタイプではないと判断ポケモンの数: 27匹
誤ってでんきタイプと判断したポケモンの数: 3匹
誤ってはがねタイプと判断したポケモンの数: 14匹
誤ってタイプ判定したポケモンの数: 14匹
検査合計結果:92匹
識別率:78.261%

■k値調整

k値を調整するにあたり、1~100の間でk値を変化させてみた。
合わせて、k値を変化させたときのtest結果を知りたかったので、pyplotによりグラフ化してみました。
今回新規追加したコードはこちらになります。

    accuracy_list = []
    check_result = 0
 k_range = range(1,100)
    for i in k_range:
        lr = KNeighborsClassifier(n_neighbors = i)
        lr.fit(X_train, y_train)
        Y_pred = lr.predict(X_test)
        accuracy_list.append(metrics.accuracy_score(y_test, Y_pred))
        if check_result < metrics.accuracy_score(y_test, Y_pred):
            check_result = metrics.accuracy_score(y_test, Y_pred)
            check_X = i
    figure = plt.figure()
    ax = figure.add_subplot(111)
    ax.plot(k_range,accuracy_list)
    plt.show()
    print("-------------------------------------------------")
    print("result k:" + str(check_X))
    print("result score: %.3f" % check_result)
    print("-------------------------------------------------")

こちらによりk値に1~100までを変化させながら予測していきます。
knnモデルでトレーニングデータを用いてフィッテングさせ、テストデータにより予測しています。

    for i in k_range:
        lr = KNeighborsClassifier(n_neighbors = i)
        lr.fit(X_train, y_train)
        Y_pred = lr.predict(X_test)

こちらの結果をaccuracy_listにそれぞれ格納していきます。
そして、

    figure = plt.figure()
    ax = figure.add_subplot(111)
    ax.plot(k_range,accuracy_list)
    plt.show()

によりグラフを生成しています。
※グラフ生成にはpyplotが必要になりますので、

import matplotlib.pyplot as plt

が必要です。

結果はこちらです。
f:id:Elsammit:20200929221211p:plain

-------------------------------------------------
result k:8
result score: 0.821
-------------------------------------------------

kが8の時に最大で82%となるようです。
まじか。。。😨
k値は調整出来ていた。。。調整出来てこれだったのか。。。

■テスト、トレーニングデータ割合調整

k値だけではだめだった。。。
では次にtrain_test_splitのパラメータである、test_sizeを調整しました。
先ほどの結果からk値は高すぎると確率が大幅に減少するため、1~10までに変更しました。
さらに、k値を調整しただけの結果よりも低いものは確認しても仕方がないので、80%以上の結果だけ出力させました。

check_random = 0
    for j in range(1,10):
        check_result = 0
        check_X = 0
        k_range = range(1,10)
        accuracy_list = []
        sss = 0.1 * j
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=sss, random_state=0)
        for i in k_range:
            lr = KNeighborsClassifier(n_neighbors = i)
            lr.fit(X_train, y_train)
            Y_pred = lr.predict(X_test)
            accuracy_list.append(metrics.accuracy_score(y_test, Y_pred))
            if check_result < metrics.accuracy_score(y_test, Y_pred):
                check_result = metrics.accuracy_score(y_test, Y_pred)
                check_X = i
                check_random = j
        if metrics.accuracy_score(y_test, Y_pred) > 0.80:
            figure = plt.figure()
            ax = figure.add_subplot(111)
            ax.plot(k_range,accuracy_list)
            plt.show()
            print("-------------------------------------------------")
            print("random state:" + str(check_random))
            print("result k:" + str(check_X))
            print("result score: %.3f" % check_result)
            print("-------------------------------------------------")

結果はこちら!!
test_size = 0.1の場合にこちらの結果となり、k=1にすると90%を超えることがわかりました。
f:id:Elsammit:20200929222902p:plain

-------------------------------------------------
result k:1
result score: 0.917
-------------------------------------------------

では、毎度のことですが、ポケモン剣盾に出現するポケモンのみに対して、タイプ推定を行います!!
。。。結果はこちら!!

合計ポケモン数:92匹
正しくでんきタイプと判断したポケモンの数: 26匹
正しくはがねタイプと判断したポケモンの数: 24匹
正しくでんきタイプではないと判断ポケモンの数: 30匹
誤ってでんきタイプと判断したポケモンの数: 1匹
誤ってはがねタイプと判断したポケモンの数: 9匹
誤ってタイプ判定したポケモンの数: 9匹
検査合計結果:92匹
識別率:86.957%

やった!!識別率85%越え👍

■最後に

今回の結果からモデルを選択するだけでなくちゃんとパラメータ調整も必要だという言うことが身に染みて分かりました!!
ちょっと機械学習に対する知識や技術が足りない。。。足りなさすぎる😢
こちらの本を購入してみました!!勉強したこともこちらで報告出来ればいいな!!と考えております。

ゼロから作るDeep Learning ?Pythonで学ぶディープラーニングの理論と実装

新品価格
¥3,445から
(2020/9/29 22:40時点)


sklearnを用いた多クラス分類試してみた

前回、ロジスティック回帰にてポケモンのステータスからタイプを推定してみました。
elsammit-beginnerblg.hatenablog.com

しかし、2クラス分類しか出来ていなかったので2種類のタイプ中、どちらのタイプが合致するかを推定することしか出来ませんでした。
そこで、今回は多クラス分類で3タイプ以上の分類を行いたいと思います!!
f:id:Elsammit:20200927222549p:plain

。。。と考えていたのですが、、、
3クラスで微妙な結果となってしまったので、今回はそこまでをまとめたいと思います😢

■ロジスティック回帰での多クラス分類

ロジスティック回帰での多クラス分類を行う上で、one vs all(one vs rest)を用います。
one vs allとは、複数クラスを「注目するクラス」と「その他のクラス」に分け、この2クラスに対してロジスティック回帰を行う手法となります!!

まずは3クラス分類するために目的変数Yを追加します。
目的変数Yを用意する関数は下記としました。

def type_to_num(p_type,test,typ):
    if p_type == test:
        return 0
    elif p_type == typ:
        return 2
    else:
        return 1

poke1_type1 = df[df['タイプ1'] == ctype1]
poket1_ype2 = df[df['タイプ2'] == ctype1]
poke1_type = pd.concat([poke1_type1, poket1_ype2])

poke2_type1 = df[df['タイプ1'] == ctype2]
poke2_type2 = df[df['タイプ2'] == ctype2]
poke2_type = pd.concat([poke2_type1,poke2_type2])

poke3_type1 = df[df['タイプ1'] == ctype3]
poke3_type2 = df[df['タイプ2'] == ctype3]
poke3_type = pd.concat([poke3_type1,poke3_type2])

pokemon_types = pd.concat([poke1_type, poke2_type, poke3_type], ignore_index=True)
type1 = pokemon_types["タイプ1"].apply(type_to_num,test=ctype1,typ=ctype3)
type2 = pokemon_types["タイプ2"].apply(type_to_num,test=ctype1,typ=ctype3)
pokemon_types["type_num"] = type1*type2
pokemon_types.head()

※ctype1~ctype3には各タイプ名が入ります。

前回の関数から1種類タイプを追加してtype_numを0~2の3種類が入るようにしました。

そして、3クラスのロジスティック回帰を実行します。
実行するコードはこちら。

X = pokemon_types.iloc[:, 7:13].values
y = pokemon_types["type_num"].values

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)
lr = LogisticRegression(C=1.0, max_iter=200)
lr.fit(X_train, y_train)

3種類分のポケモンを分類することになり、繰り返し回数が増大したため、max_iterを200に設定しました。


こちらで試しに、「はがね」、「ノーマル」、「みず」の組み合わせで実行したところ、、、

-------------------------------------------------
trainデータに対するscore: 0.618
testデータに対するscore: 0.627
-------------------------------------------------

となりました!!
低い!! 60%台って。。。流石に。。。きつい。。。

一番判別できる組み合わせを確認してみました!!
結果、
「はがね」、「ノーマル」、「みず」の組み合わせが一番高い結果となりました。
ですが、

Highest Score is ...
========================================================
type1 is でんき type2 is ノーマル type3 is はがね
result score is 0.829
result score(train) is 0.795
========================================================

という結果であり、80%台👌
まぁ、及第点かな?

次に前回と同様にポケモン剣盾で出現するポケモンのみに限定して先ほど学習させたモデルで予測判定を実施してみました!!
検証時のコードはこちら。

poketype1 = "でんき"
poketype2 = "ノーマル"
poketype3 = "はがね"

    with codecs.open("/kaggle/input/pokemon/pokemon.csv", "r", "utf-8", "ignore") as file:
        check = pd.read_table(file, delimiter=",")  
    
    metal11 = check[check['type1'] == poketype1]
    metal22 = check[check['type2'] == poketype1]
    metal = pd.concat([metal11, metal22])

    elec1 = check[check['type1'] == poketype2]
    elec2 = check[check['type2'] == poketype2]
    elec = pd.concat([elec1,elec2])
    
    water1 = check[check['type1'] == poketype3]
    water2 = check[check['type2'] == poketype3]
    water = pd.concat([water1,water1])

    pokemon_check = pd.concat([metal,elec,water], ignore_index=True)
    type11 = pokemon_check["type1"].apply(type_to_num,test=poketype1,typ=poketype3)
    type22 = pokemon_check["type2"].apply(type_to_num,test=poketype1,typ=poketype3)
    pokemon_check["type_num"] = type11*type22
    pokemon_check.head()

    X = pokemon_check.iloc[:, 1:7].values
    y = pokemon_check["type_num"].values

    i = 0
    error1 = 0
    success1 = 0
    error2 = 0
    success2 = 0
    error3 = 0
    success3 = 0
    print("[判断したポケモン結果]")
    print("----------------------------------------")
    print("")

    while i < len(pokemon_check):
        y_pred = lr.predict(X[i].reshape(1, -1))
        if y_pred == 0:
            if pokemon_check.loc[i, ["type_num"]].values == 0:
                success1 += 1
            else:
                error1 += 1
                print(pokemon_check.loc[i, "name"])
                print(str(poketype1)+"タイプではないです")
                print("")
        elif y_pred == 1:
            if pokemon_check.loc[i, ["type_num"]].values == 0:
                error2 += 1
                print(pokemon_check.loc[i, "name"])
                print(str(poketype1)+"タイプです")
                print("")
            elif pokemon_check.loc[i, ["type_num"]].values == 2:
                error2 += 1
                print(pokemon_check.loc[i, "name"])
                print(str(poketype3)+"タイプです")
                print("")             
            elif pokemon_check.loc[i, ["type_num"]].values == 1:
                success2 += 1
        elif y_pred == 2:
            if pokemon_check.loc[i, ["type_num"]].values == 2:
                success3 += 1
            else:
                error3 += 1
                print(pokemon_check.loc[i, "name"])
                print(str(poketype3)+"タイプではないです")
                print("")

        else:
            print("意味不エラー")
        i += 1
    print("----------------------------------------")
    print("正しく" + str(poketype1) + "タイプと判断したポケモンの数: %d匹" % success1)
    print("正しく" + str(poketype3) + "タイプと判断したポケモンの数: %d匹" % success3)
    print("正しく" + str(poketype1) + "タイプではないと判断ポケモンの数: %d匹" % success2)
    print("誤って" + str(poketype1) + "タイプと判断したポケモンの数: %d匹" % error1)
    print("誤って" + str(poketype3) + "タイプと判断したポケモンの数: %d匹" % error2)
    print("誤ってタイプ判定したポケモンの数: %d匹" % error2)
    print("検査合計結果:%d匹" % (error1+error2+error3+success1+success2+success3))
    print("識別率:%.3f%%" % ((success1+success2+success3)/(error1+error2+error3+success1+success2+success3)*100))

上記を実行した結果、

----------------------------------------
合計ポケモン数:92匹
正しくでんきタイプと判断したポケモンの数: 22匹
正しくはがねタイプと判断したポケモンの数: 20匹
正しくでんきタイプではないと判断ポケモンの数: 29匹
誤ってでんきタイプと判断したポケモンの数: 4匹
誤ってはがねタイプと判断したポケモンの数: 15匹
誤ってタイプ判定したポケモンの数: 15匹
検査合計結果:92匹
識別率:77.174%

70%台😂 あまりうまく判定出来ていないですね泣。

■k近傍法で分類してみた

あまりうまく行かなったので、今度はk近傍法で分類を試してみました。


クラス判別法の1つ。knnと呼ばれております。
学習データをベクトル空間上にプロットしておき、未知のデータが得られたら、そこから距離が近い順に任意の個数を取得し、
多数決でデータが属するクラスを推定する。

例えば下記の場合、
k=3の範囲では、ClassBに分類
k=6の範囲では、ClassAに分類
されることになります。
f:id:Elsammit:20200927214041p:plain

knnはこちらの要領で実行可能です。

from sklearn.neighbors import KNeighborsClassifier
from sklearn.cross_validation import train_test_split

lr = KNeighborsClassifier(n_neighbors = 1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)
lr.fit(X_train, y_train)

ロジスティック回帰と異なる点は、

from sklearn.neighbors import KNeighborsClassifier

を定義することと、
モデルを

lr = KNeighborsClassifier(n_neighbors = 8)

に置き換えることです。
n_neighborsにはk値が入ります。

knnで実行してみた結果、

-------------------------------------------------
trainデータに対するscore: 0.769
testデータに対するscore: 0.821
-------------------------------------------------

となり、80%台👌

先ほどと同様に、ポケモン剣盾で出現するポケモンのみに限定して先ほど学習させたモデルで予測判定を実施!!

----------------------------------------
合計ポケモン数:92匹
正しくでんきタイプと判断したポケモンの数: 23匹
正しくはがねタイプと判断したポケモンの数: 22匹
正しくでんきタイプではないと判断ポケモンの数: 27匹
誤ってでんきタイプと判断したポケモンの数: 3匹
誤ってはがねタイプと判断したポケモンの数: 14匹
誤ってタイプ判定したポケモンの数: 14匹
検査合計結果:92匹
識別率:78.261%

70%台😂 先ほどのロジスティック回帰と比較すれば若干よく判定出来ていますが、、、あまりうまく判定出来ていないですね泣。

ソースコードはこちらに公開しております。
https://github.com/Elsammit/PokemonAI.git

■今後の方針

2クラス分類と比較してとても微妙な結果となりました。。。
k近傍法(knn)、ロジスティック回帰どちらもほぼ同じ結果。。。
ただ、パラメータを変化させていないので、こちらを最適化させることでもう少し精度を上げることができそうな気がします。
引き続き調査してみたいと思います!!

■最後に

クラスが1つ追加するだけで難易度がグッと上がった気がします😢
後、色々ネットを見ながら進めてきましたが、結構苦しいですね。
やはり本などでベースを作らなければならないですね。。。
G検定合格したので各アルゴリズムの概要は知っているのですが、いざ使うとなると。。。ですね

深層学習教科書 ディープラーニング G検定(ジェネラリスト) 公式テキスト

新品価格
¥2,772から
(2020/9/27 22:22時点)

以前から気になっていたこちらの本でも買って読もうかな??

ゼロから作るDeep Learning ?Pythonで学ぶディープラーニングの理論と実装

新品価格
¥3,445から
(2020/9/27 22:20時点)

■参考
http://www.tsjshg.info/udemy/Lec80-81.html
K近傍法(多クラス分類) - Qiita

sklearnを用いたロジスティク回帰(LogisticRegression)

先日Kaggleを使えるようにしました。
せっかくなので、Notebooksを用いて、ロジスティク回帰を試してみようと思います!!
データは前回も用いているポケモンステータスがまとめてあるcsvファイルを用います!!

■ロジスティック回帰とは?

目的変数が2値の時に利用する回帰のこと。
例えば、この人は購入するかしないか、やお昼を食べたか否か、などの2択(2値)に対して予測を行うための回帰曲線の1つです。
ロジスティック回帰はこのような図になります。
f:id:Elsammit:20200926162959p:plain


計算式は
f:id:Elsammit:20200926163214p:plain
で表せます。

ロジスティック回帰は下記コードを実行するとグラフが表示されます。

import numpy as np
import matplotlib.pyplot as plt
 
# ロジスティック関数
def logistic_function(x):
    return 1/ (1+ np.exp(-x))
 
# プロットの作成
x = np.linspace(-10,10,1000)
plt.plot(x, logistic_function(x))
plt.title("logistic function")
plt.xlabel("x")
plt.ylabel("y")

■Scikit-learnを用いたロジスティク回帰

まずScikit-learnですが、機械学習のライブラリです。
Scikit-learnの公式ドキュメントは下記になります。
scikit-learn: machine learning in Python — scikit-learn 0.23.2 documentation

Scikit-learnでロジスティック回帰を行う場合には、
linear_modelのLogisticRegressionモデル
を用いればよいです。

pythonでScikit-learnでのロジスティック回帰を用いる場合には、下記をimportする必要があります。

from sklearn.linear_model import LogisticRegression

■ロジスティク回帰を用いたポケモン識別率評価

今回は例として、ノーマルとはがねタイプでの分類についてまとめたいと思います。
ソースコード全体はこちらとなります。

import pandas as pd
from pandas import plotting  
import codecs
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

#目的変数yのための項目を追加するための関数.
def type_to_num(p_type,test):
    if p_type == test:
        return 0
    else:
        return 1

def metal_normal():

    # ポケモンステータスがまとめられたcsvファイルを読み出し.
    with codecs.open("/kaggle/input/pokemon-status/pokemon_status.csv", "r", "utf-8", "ignore") as file:
        df = pd.read_table(file, delimiter=",")  
    
    # はがねタイプのポケモンを抽出.
    metal1 = df[df['タイプ1'] == "はがね"]
    metal2 = df[df['タイプ2'] == "はがね"]
    metal = pd.concat([metal1, metal2])

   # ノーマルタイプのポケモンを抽出.
    normal1 = df[df['タイプ1'] == "ノーマル"]
    normal2 = df[df['タイプ2'] == "ノーマル"]
    normal = pd.concat([normal1,normal2])

    # ノーマル +はがねタイプのポケモンリストを作成し、追加ではがねタイプか否かを判定するためのフラグを項目に追加.
    pokemon_m_n = pd.concat([metal, normal], ignore_index=True)
    type1 = pokemon_m_n["タイプ1"].apply(type_to_num,test="はがね")
    type2 = pokemon_m_n["タイプ2"].apply(type_to_num,test="はがね")
    pokemon_m_n["type_num"] = type1*type2
    pokemon_m_n.head()

    # X軸にポケモンのステータスを抽出。 y軸にはがねタイプか否かの判定結果を登録.
    X = pokemon_m_n.iloc[:, 7:13].values
    y = pokemon_m_n["type_num"].values

    # 訓練データとテストデータに分割
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)
    
    #ロジスティックモデル生成
    lr = LogisticRegression(C=1.0)
    
    #訓練データを用いてロジスティック回帰をフィッティング
    lr.fit(X_train, y_train)
     
    #判定結果
    print("-------------------------------------------------")
    print("trainデータに対するscore: %.3f" % lr.score(X_train, y_train))
    print("testデータに対するscore: %.3f" % lr.score(X_test, y_test))
    print("-------------------------------------------------")

metal_normal()

まず、csvファイルからポケモンステータスのリストを収集し、その中ではがねタイプもしくはノーマルタイプをポケモンを抽出します。
抽出にはpandasを用いました。

    # ポケモンステータスがまとめられたcsvファイルを読み出し.
    with codecs.open("/kaggle/input/pokemon-status/pokemon_status.csv", "r", "utf-8", "ignore") as file:
        df = pd.read_table(file, delimiter=",")  
    
    # はがねタイプのポケモンを抽出.
    metal1 = df[df['タイプ1'] == "はがね"]
    metal2 = df[df['タイプ2'] == "はがね"]
    metal = pd.concat([metal1, metal2])

   # ノーマルタイプのポケモンを抽出.
    normal1 = df[df['タイプ1'] == "ノーマル"]
    normal2 = df[df['タイプ2'] == "ノーマル"]
    normal = pd.concat([normal1,normal2])

次に抽出したノーマルタイプとはがねタイプとして抽出したリストを結合し、
結合したリストに目的変数yのためにはがねタイプか否かを判断するためのフラグ項目を追加します。
目的変数yを追加する処理は下記となります。

def type_to_num(p_type,test):
    if p_type == test:
        return 0
    else:
        return 1

type1 = pokemon_m_n["タイプ1"].apply(type_to_num,test="はがね")
type2 = pokemon_m_n["タイプ2"].apply(type_to_num,test="はがね")
pokemon_m_n["type_num"] = type1*type2

これで機械学習を行うためのデータ加工が完了しました。

次にロジスティック回帰モデルでの機械学習を行うために訓練データとテストデータを分割します。
分割する処理は下記となります。

    # X軸にポケモンのステータスを抽出。 y軸にはがねタイプか否かの判定結果を登録.
    X = pokemon_m_n.iloc[:, 7:13].values
    y = pokemon_m_n["type_num"].values

    # 訓練データとテストデータに分割
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)

test_sizeが用意したデータ内でテストデータとして用いる割合を示しております。

最後にロジスティック回帰モデルで機械学習を行い、テストデータによる解析結果を実施。

    #ロジスティックモデル生成
    lr = LogisticRegression(C=1.0)
    
    #訓練データを用いてロジスティック回帰をフィッティング
    lr.fit(X_train, y_train)
     
    #判定結果
    print("-------------------------------------------------")
    print("trainデータに対するscore: %.3f" % lr.score(X_train, y_train))
    print("testデータに対するscore: %.3f" % lr.score(X_test, y_test))
    print("-------------------------------------------------")


ノーマルとはがねタイプの分類結果ですが、

-------------------------------------------------
trainデータに対するscore: 0.954
testデータに対するscore: 0.943
-------------------------------------------------

となりました!!
かなり高い確率でノーマルタイプとはがねタイプを分類することが出来ました!!

■別データ群を用いて実際に識別したらどうなる?

先ほどの結果により、ロジスティック回帰モデルでノーマル・はがねタイプをかなり正確に分類させることが出来ました。

実は今回用いたcsvファイルには最新ゲームであるポケットモンター剣盾(剣盾)のデータは入っておりません。
では、今回のモデルを剣盾に出現するポケモンのステータスデータに適用した場合の識別率を評価したいと思います!!

先ほどの判定モデル生成コードに続けて下記を追記下さい。
※剣盾に出現するポケモンのデータが格納されたcsvファイル名は"pokemon.csv"とします。

    with codecs.open("pokemon.csv", "r", "utf-8", "ignore") as file:
        check = pd.read_table(file, delimiter=",")  
    
    metal11 = check[check['type1'] == "はがね"]
    metal22 = check[check['type2'] == "はがね"]
    metal = pd.concat([metal11, metal22])

    elec1 = check[check['type1'] == "ノーマル"]
    elec2 = check[check['type2'] == "ノーマル"]
    elec = pd.concat([elec1,elec2])

    pokemon_check = pd.concat([metal,elec], ignore_index=True)
    type11 = pokemon_check["type1"].apply(type_to_num,test="はがね")
    type22 = pokemon_check["type2"].apply(type_to_num,test="はがね")
    pokemon_check["type_num"] = type11*type22
    pokemon_check.head()

    X = pokemon_check.iloc[:, 1:7].values
    y = pokemon_check["type_num"].values

    i = 0
    error1 = 0
    success1 = 0
    error2 = 0
    success2 = 0
    print("[はがねタイプと判断したポケモン一覧]")
    print("----------------------------------------")
    print("")

    while i < len(pokemon_check):
        y_pred = lr.predict(X[i].reshape(1, -1))
        if y_pred == 0:

            if pokemon_check.loc[i, ["type_num"]].values == 0:
                success1 += 1
            else:
                error1 += 1
                print(pokemon_check.loc[i, ["name"]])
                print("はがねタイプではないです")
                print("")
        else:
            if pokemon_check.loc[i, ["type_num"]].values == 0:
                error2 += 1
                print(pokemon_check.loc[i, ["name"]])
                print("はがねタイプです")
                print("")
            else:
                success2 += 1
        i += 1
    print("----------------------------------------")
    print("正しくはがねタイプと判断したポケモンの数: %d匹" % success1)
    print("正しくはがねタイプではないと判断ポケモンの数: %d匹" % success2)
    print("誤ってはがねタイプと判断したポケモンの数: %d匹" % error1)
    print("誤ってはがねタイプではないと判断したポケモンの数: %d匹" % error2)
    print("識別率:%.3f%%" % ((success1+success2)/(error1+error2+success1+success2)*100))

下記までは、剣盾のポケモンデータを読み出して結合して~~なので割愛!!

 ~~~~
    X = pokemon_check.iloc[:, 1:7].values
    y = pokemon_check["type_num"].values

こちらで、剣盾で出現するノーマル or はがねタイプのポケモンデータから、
はがねタイプと識別できるか?はがねタイプではないと識別できるか?
を各ポケモンで判定するコードになります。

    while i < len(pokemon_check):
        y_pred = lr.predict(X[i].reshape(1, -1))
        if y_pred == 0:
            if pokemon_check.loc[i, ["type_num"]].values == 0:
                success1 += 1
            else:
                error1 += 1
                print(pokemon_check.loc[i, ["name"]])
                print("はがねタイプではないです")
                print("")
        else:
            if pokemon_check.loc[i, ["type_num"]].values == 0:
                error2 += 1
                print(pokemon_check.loc[i, ["name"]])
                print("はがねタイプです")
                print("")
            else:
                success2 += 1
        i += 1

それぞれ、
 ・success1:はがねタイプと正しく判定出来た場合に加算
 ・success2:はがねタイプではないと正しく判定出来た場合に加算
 ・error1:はがねタイプではないのにはがねタイプと判定してしまった場合に加算
 ・error2:はがねタイプなのにはがねタイプではないと判定してしまった場合に加算
となります。

ここで、ポケモンのステータスから目的変数yを想定するための処理は、

y_pred = lr.predict(X[i].reshape(1, -1))

で実行しております。

本処理を実行した結果、

-------------------------------------------------
[はがねタイプと判断したポケモン一覧]
----------------------------------------

name    ドーミラー
Name: 3, dtype: object
はがねタイプです

name    ガラルニャース
Name: 6, dtype: object
はがねタイプです

name    ゾウドウ
Name: 9, dtype: object
はがねタイプです

name    ダイオウドウ
Name: 10, dtype: object
はがねタイプです

name    アーマーガア
Name: 15, dtype: object
はがねタイプです

name    ドリュウズ
Name: 16, dtype: object
はがねタイプです

name    ガラルマッギョ
Name: 19, dtype: object
はがねタイプです

name    トゲデマル
Name: 25, dtype: object
はがねタイプです

name    ザシアン
Name: 26, dtype: object
はがねタイプです

name    バイウールー
Name: 36, dtype: object
はがねタイプではないです

name    ジジーロン
Name: 51, dtype: object
はがねタイプではないです

----------------------------------------
正しくはがねタイプと判断したポケモンの数: 19匹
正しくはがねタイプではないと判断ポケモンの数: 31匹
誤ってはがねタイプと判断したポケモンの数: 2匹
誤ってはがねタイプではないと判断したポケモンの数: 9匹
識別率:81.967%

識別率が80%台まで落ちてしまいました。。。泣
誤ってはがねタイプと判断したポケモンの数2匹に対して、
はがねタイプなのに誤ってしまったポケモンが9匹なので、正しくはがねタイプと判定出来ていないこと原因??
どうすればよいのか、までは分かっていない。。。


■最後に
初心者なりにロジスティック回帰モデルでポケモンのタイプ識別が行えました!!
はがねタイプとノーマルタイプを選んだ理由ですが、
実はpokemon_status.csvファイルで用意されたポケモンデータから識別率の高いタイプ組を選出しましたw。

ただ、識別率を上げるためにどうすればよいのか分からないため、更なる勉強が必要!!
これから機械学習も勉強していきます!!

■参考
ポケモンで学ぶ機械学習 - Qiita
Scikit-learn でロジスティック回帰(クラス分類編) - Qiita
ロジスティック回帰 - Qiita

Kaggleに登録してみました

OpenCVを触っていくうちに画像処理系の機械学習をもう少し学んだり発信していきたいなと思ってきました。

それで、何かいい方法ないかな?と調べてみたところ、
f:id:Elsammit:20200924230350p:plain
という、機械学習をメインに取り扱うデータサイエンスに携わっている人々のコミュニティを見つけました。


ちょっと自分には早いかな?とも思ったのですが、、、
初心者歓迎的な感じだったので、実際に登録して触ってみたのでちょっと報告です。

■Kaggleとは?

Kaggleとは、世界最大の機械学習コンペティションのプラットフォームです。
企業や研究機関などが提供するデータについて、世界中から集まる参加者が機械学習モデルの性能を競います。
同時に、界中の機械学習・データサイエンスに携わっている約40万人の方が集まるコミニティーとなっているようです。
コンペティションは、企業や政府がコンペ形式(競争形式)で課題を提示し、賞金と引き換えに最も制度の高い分析モデルを買い取るという、最近でいう一種のクラウドファンディングに近いような仕組みとなります。

このコンペティションですが、Kaggleに参加している人たちは無料で参加可能です。
さらにさらに!!
レーニング用のデータセットや必要なデータは企業から提供されており、テストセットでの評価までも可能になっております!!

まぁ、まだコンペティションに参加するまでの技術力はないのでまだまだ先の話なんですけどね(-_-;)


そんな自分のような初心者でも、Kaggleはおすすめとなっております!!
Kaggleには
 ・Notebooks
 ・Courses
の機能があります。
これらの機能を閲覧したり、実際に触ったりすることで少しずつ機械学習を学ぶことが出来るようになっており、
最終的にはコンペティションに参加出来るまでの技術力が身に付きそうです!!

■Coursesとは?

pythonコードの書き方から機械学習入門、pandasの使い方などの学習コースが受けられます。
こちらは基本的に無料で受講可能なようです!!
※作成はKaggleに参加しているデータサイエンティストの方々のようです。

但し、全て英語で記載されていますのである程度の英語力が必要になってしまいます(-_-;)
私、、、英語出来ないんですよね泣。
機械学習入門を受講しているのですが、結構英語の壁にぶつかっております泣。

■Notebooksとは?

Notebooksは、PythonやRなどの言語を使い、自分で作成したプログラムを実行することが出来る環境が用意された機能となります。
このNotebooksのメリットは何といっても
無料で機械学習が実行できる最低限の環境が構築不要で提供されている
ことです!!
勉強したことや試してみたい機械学習を実施するには問題ない環境が無料で使用可能です!!

また、他の人が書いて公開されているコードや説明が閲覧可能です。
先ほど記載した通り、40万人のデータサイエンティストの方々がKaggleに参加しており、
その方々の説明やコードを見るだけでもかなりの勉強にもなります!!

Notebooksのコードや説明は公開/非公開で切り替えられるようになっているため、
自分で公開したい内容に限定して公開することが可能です。
また、公開していると他者からコメントとかも届くようです。

勉強にはもってこいですね。

■Notebooks使ってみた

KaggleにログインするとこちらのようなHome画面が表示されます。
左端のNotebooksをクリックすると、こちらのような画面が表示されます。
f:id:Elsammit:20200924223555j:plain

publicタブでは他の方が公開にしているコードや説明を閲覧することが出来ます。
pandasなどのライブラリの備忘録を作成している人やYOLOなどの実際の機械学習を書いている人など様々です。
閲覧だけでも勉強になるので、よさげ。

まだ機能の利用は出来ていないですが、公開されているコードをコピーしたりAPIとして自分の環境上で編集することも出来るようです。
ベースとして利用することで、自分の実施したい内容に集中できそうです。



Your Worksタブには自分が作成している非公開・公開の環境(コードや説明)の一覧が表示されます。
また、Course機能で受講時に作成したコードもこちらで見るとことが出来るようです。


Notebooks新規作成ボタンがあります。
新規ボタンを押下すると、
f:id:Elsammit:20200924224632j:plain
といった画面が表示されます。
Select Languageにて、「Python」もしくは「R」のどちらかを選択できます。
また、エディターとして、「Notebook」もしくは「Scripts」のどちらかを選択できます。
Notebookはjupiter notebookのようなエディター形式となっております。

Select Languageやエディターについてはお好みで選択ください。

Notebookを選択した場合にはこのような画面になります。
f:id:Elsammit:20200924225439j:plain

左の▶をクリックするとコードの実行が行われます。
また、右タブの+Add Dataクリックするとデータのアップロードが可能です。
Upload DataSet ⇒ Select Files to Uploadsを選択すると自分のローカル環境のファイルをアップロードすることが出来ます。
このアップロードファイルも公開/非公開を切り替えることが出来るようです。

■最後に

これから機械学習を勉強するためにkaggleを有効利用していきたいと思います!!
ただ、ほぼ全て英語なので、、、英語も出来るようにならないとですね(-_-;)
英語の勉強もしていかなきゃ!!
先輩に進められたこちらの本をまずは読んで勉強しようかな??

速読速聴・英単語 Core1900 ver.5 (速読速聴・英単語シリーズ)

新品価格
¥2,090から
(2020/9/24 23:01時点)


アジャイル開発 SCRUM BOOTCAMPを読んで


突然ですが、会社でアジャイル開発やるようなんですが、、、
自分アジャイル開発やったことないんですよね(-_-;)

アジャイル開発は基本情報受験のために勉強した程度でどんな開発か詳しく分からないんですよね。
テスト駆動やペアプログラミングやって短期間で動くものを作るんでしょ?ぐらいしか知らない状態でした。。。

こんな状態ではダメだと思い、会社の先輩に教えてもらったこちらの本を読みました!!

SCRUM BOOT CAMP THE BOOK【増補改訂版】 スクラムチームではじめるアジャイル開発

新品価格
¥2,376から
(2020/9/23 22:26時点)

内容としては基礎編、実践編に分かれており、
基礎編は、基本的な用語の説明がメインでした。
スクラムとは何か?からスクラムチーム内での各ロールの役割、アジャイル開発の作成物であるプロダクトバックログやスプリントバックログ
などの説明が書かれておりました。
後は、簡単なアジャイル開発での進めた・やり方が記載されています。


実践編は実際の開発ケースを想定して、実際にアジャイル開発を行った場合に各タイミングで発生する問題や困り事に対してどうやって解決すればよいのか、
が記載されています。
例えば、
 ・アジャイル開発を行う上でどのように工数を見積ればよいの?
 ・スプリントが順調に進んでいるかどうやって判断すればよいの?
 ・スプリントって延ばしてもよいの?
 ・全員揃わない状態でのイベントを行ってもよいの?
 ・人数を増やせば短縮させることはできるの?
などが丁寧にかつ分かりやすく記載されております。

この本の良いところは実際のケースは漫画で描かれているため各困り事や問題が理解しやすくなっていた点と、
初心者にも分かりやすくかみ砕いた説明になっていた点です!!
自分も本を読み終わった後はアジャイル開発について分かった気がします!!

しかも、本当に実際の開発ケースに沿っているので、
「うんうん、確かにこんなことあるよね」
っていうことが多々あり、その時の解決策が載っているのは、とても分かりやすかったです!!
アジャイル開発を進める上で困ったらまた読み直したい一冊です!!

この本の最後に、
「毎日の活動を通じて体験の中で学んでいく」
スクラムは体験して学んでいく仕組み」
と記載されています。

この本を読んだ時にまさしくその通りだな!!と納得しました。
確かに、自分達が定めたゴールに向けて、現場で仕様をフレキシブルしてスプリントを回しながらソフト開発を行っていくのだが、
同時にゴールに向けてスクラムチーム内の各個人が問題や検討を自分事と考え、意見をぶつけ合い、助け合いながら、
完璧なチームに向けて成長していく、
そんな開発形態なんだな。と思いました!!

大まかなやり方は決まっているが、細かいやり方はスクラムチームのやりたいようにカスタマイズすることが必要で、
始めはうまく行かなくても、「なぜうまく行かないのか?」をチームで考えながら、活動・体験しながら成長していくことが
アジャイル開発の必要なことなんだな、と思いました。


と、長々と記載してしまいました。。
後でアジャイル開発の概要についてまとめようかな?と思います!!
細かいところはチーム内で話し合うことが重要なので、実際にアジャイル開発になったらチームで思いっきり議論したいと思います!!