20190209のReactに関する記事は3件です。

バレンタインデーにもらったチョコが本命かどうか判定するAIを実際に作って公開した

TL;DR

バレンタインデーが近いですね!
バレンタインでもらったチョコが、本命かどうかは、世の男性にとって、重大な問題です。
女性心理の機微に疎い男子中高生、理系男子学生、男性エンジニア(偏見) を救うべく、実際にもらったチョコが本命かどうか判定するAIを開発しました

apptop-min.png

1. 男性エンジニアを悩ませるバレンタイン問題

1.1 バレンタインデー

世界中にあるイベントの一つに、2/14のバレンタインデー(Valentine’s Day)があります。
国や地域によって、その日の過ごし方などは異なるようですが、ここ日本では、女性から男性へチョコレートを贈る日、という お菓子会社の戦略によって定着した文化 があります(例えば[1]参照。諸説あり)。

[1]によれば、1970年代後半頃に本命チョコを贈る習慣が定着したようです。ここから、さらにお菓子業界の商機ととらえた全国飴菓子工業協同組合は、義理チョコとホワイトデーという文化を創り出し、1984年をホワイトデー定着の年としました。

1.2 バレンタインデーの悲劇

しかし、このような文化は悲劇を起こしました。それは、女性から贈られたチョコが、本命なのか義理なのかがはっきりしないケースが登場したためです。はっきり口にせず、以心伝心や忖度を是 とする日本の文化が、多くの迷える男性を生み出して しまうことになりました。

このような背景から、贈られたチョコレートが、本命なのか義理なのかをはっきりさせる社会的ニーズが産まれました。図1に、この状況証拠を示します。世の中のどれだけの人が検索をすれば、Googleの検索サジェスチョンにこのような結果をもたらすのでしょうか。これは、もはや国民的課題といえるでしょう。
以降、これを バレンタイン問題 と呼ぶことにします。

図1
図1:バレンタイン問題 – バレンタインのチョコレートが本命か義理かを判断する社会的ニーズ

1.3 バレンタインデーの救世主

一方、最近の機械学習手法の発達は目覚ましい物があります。AIすなわち、機械学習がこの バレンタイン問題 を解決する手段になりえることが示されました[2]。これは画期的な観点であると思われましたが、開発したエンジニアのみが実行、評価できるものでした。これでは、迷える男子中高生、理系男子大学生、そして男性エンジニアを救う ことはできません(筆者の独断と偏見による)。

また、男性の立場からすると、ホワイトデーにお返しを行う義務が、暗黙のうちに発生します。ここで不適切な対応をとると、その後の長期間にわたり、生きづらい人生を歩むことになりかねません。
そのため、贈られたチョコレートに対して、どのくらいのお返しを行えばよいのか、その指針を示すことができれば、悩める男性に対して有効なサービスとなり得ると思われます。図2に、悩める男性像の一例を示します。


図2 バレンタインのお返しに苦悩する男性の一端

1.4 バレンタイン問題の解決方法

以上のことから、本記事では[2]をさらに発展させ、バレンタイン問題の一助となることを目指すものです。

  • 本命チョコと義理チョコを判別する学習モデルを開発する
    • 二値分類と呼ばれる、機械学習における代表的な手法を用いたアプローチをとる
    • これを本命・義理チョコ分類問題と呼ぶ
  • 開発した、構築した学習モデルをサービス化して公開する
    • PC、スマホのどちらからでも使えるように、Webサービス化を行う
  • 義理チョコと本命チョコを分類する際の確率を算出する
    • 定量的に判断指標を示すことで、悩める男性自身の判断を促す
    • 確率を用いることで、ホワイトデーのお返し予算の提案も併せて行う

実際のイメージを、以下に示します。

なお、筆者が人生でチョコレートを貰った回数(個数)は、年齢のおよそ25%以下である。
チョコ欲しい

2. DNNでの学習

以降、以下URLに、随時加筆修正していきます。
https://boomin.yokohama/archives/1357

2.1 学習データ

2.2 学習モデル

2.3 学習の実行

2.4 学習モデルの評価

3. お返し予算の算出

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSXの基本

はじめに

JSXとは、JavaScriptの拡張構文です。
実行時、内部的にはJavaScriptにトランスパイルされます。

具体的には、React.createElementメソッドに変換される

複数の要素を列挙することができない

複数の要素を列挙することができないため、以下のコードは不可です。

sample.js
React.DOM.render(
  <h1>Hello, World</h1>
  <p>こんにちは、世界</p>,
  document.getElementById('root')
);

複数の要素全体を<div>などの要素で括る

sample.js
React.DOM.render(
  <div>
    <h1>Hello, World</h1>
    <p>こんにちは、世界</p>,
  </div>
  document.getElementById('root')
);

または、<React.Fragment>で、複数の子要素をまとめる

sample.js
React.DOM.render(
  <React.Fragment>
    <h1>Hello, World</h1>
    <p>こんにちは、世界</p>,
  </React.Fragment>
  document.getElementById('root')
);

空要素は<~ />で終える

JSXでは、XMLと同じように、空要素は<~ />で終えなければなりません。

sample.js
//ダメなコード
const tag = <img src={logo}>;

//正しいコード
const tag = <img src={logo} />;

空要素とは

JavaScriptの予約語は使えない

JSXはあくまでもJavaScriptの拡張構文なので、JavaScriptの予約語であるclass, forなどの属性は使えない。

JavaScriptを使う場合は{}で埋め込む

JSX内でJavaScriptを使う場合は、{}で埋め込むことができる。

sample.js
const name = 'Tom';

ReactDOM.render(
  <p>Hello {name}.</p>,
docunent.getElementById('root')
);

#=> Hello Tom.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React.js の Context API で Private Route を作る

Reactでログイン機能があるSPAを開発する際に、特定のページ(ルート)を秘匿したい場合があると思います。
今回はReact v16から実装されたContext APIを使ってプライベートなルートを作成します。

Context APIとは

Context APIはProviderとConsumerという機構を使い、異なるコンポーネント間でデータの共有を可能にするものです。大まかな概要は以下の通りです。

  • createContextはProviderとConsumerというコンポーネントのペアを返す
  • Providerはデータとコールバックを保有するコンポーネント
  • ConsumerはProviderのデータを購読しコンポーネントにデータを渡す

環境

ライブラリのバージョン

Library version
react 16.2.0
react-router-dom 4.3.1

ディレクトリ構成

Reactではディレクトリ構成やコンポーネントの分割に規制がなく、アプリケーションの要件に応じて設計することができます。
以下は今回、解説に使うアプリケーションのディレクトリ構成です。あくまで例ですので、実際の開発では適宜設計してください。

index.js
  /contexts
    auth.js
  /routes
    home.js
    mypage.js

1. Contextを作成する

/contextsディレクトリにauth.jsファイルを作成しAuthContextを作成します。

// contexts/auth.js
import React, { Component } from 'react';
// Contextの生成
const AuthContext = React.createContext();
// ContextにはProviderとConsumerというコンポーネントが含まれる
const AuthProvider = AuthContext.Provider;
const AuthConsumer = AuthContext.Consumer;

export { AuthProvider, AuthConsumer };

AuthContextにログイン状態を表すisAuthというデータを管理させるため、Providerをカスタマイズします。

// contexts/auth.js
...
class AuthProvider extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isAuth: false
    }
  }
  // Providerのvalueを設定する。Consumerはこのvalueを購読する。
  render() {
    return (
      <AuthContext.Provider
        value={{
          isAuth: this.state.isAuth
        }}
      >
        {this.props.children}
      </AuthContext.Provider>
    );
  }
}
...

2. Contextからデータを取り出す

AuthContextからisAuthを取り出しコンポーネントに反映させてみます。
Headerというコンポーネントを作成し、ログイン状態に応じてテキストを表示させます。

ProviderでHeaderコンポーネントをラップし、コンポーネント側ではConsumerを使ってデータを取り出します。

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { AuthProvider, AuthConsumer } from './contexts/auth';
// ProviderでHeaderコンポーネントをラップ
const App = () => (
  <AuthProvider>
    <Header />
  </AuthProvider>
);
// Headerコンポーネント中でConsumerからデータを取り出す
const Header = () => (
  <AuthConsumer>
    {({ isAuth }) => (
      <>
        <h1>Header</h1>
        {isAuth ? (
          <p>ログインしているよ!</p>
        ) : (
          <p>ログインしていないよ!</p>
        )}
      </>
    )}
  </AuthConsumer>
);

ReactDOM.render(<App />, document.getElementById('root'));

3. Providerに状態を変更するコールバックを作成

ログイン、ログアウトボタンを作成するために、AuthProviderにログイン状態を切り替えるためのコールバック関数を作成します。
今回はfetch等のAPI通信は割愛します。

// contexts/auth.js
...
class AuthProvider extends Component {
  constructor(props) {
    ...
    // thisをバインド
    this.login = this.login.bind(this);
    this.logout = this.logout.bind(this);
  }
  login() {
    // 実際にはここでAPI等を使ってログイン処理を行う
    setTimeout(() => this.setState({ isAuth: true }), 1000);
  }
  logout() {
    this.setState({ isAuth: false });
  }
  render() {
    return (
      <AuthContext.Provider
        value={{
          isAuth: this.state.isAuth,
          login: this.login,
          logout: this.logout
        }}
      >
        {this.props.children}
      </AuthContext.Provider>
    );
  }
}
...

続いてHeaderコンポーネントにログイン/ログアウトボタンを実装します。

// index.js
...
const Header = () => (
  <AuthConsumer>
    {({ isAuth, login, logout }) => (
      <>
        <h1>Header</h1>
        {isAuth ? (
          <p>ログインしているよ!</p>
          <button onClick={logout}>ログアウト</button>
        ) : (
          <p>ログインしていないよ!</p>
          <button onClick={login}>ログイン</button>
        )}
      </>
    )}
  </AuthConsumer>
);
...

4. プライベートなルートを作成

次はreact-routerを使ってプライベートなルートを作成していきます。
Headerコンポーネントに続いて、react-routerを使ってルーティングを設定します。

// index.js
...
import { BrowserRouter, Route, Redirect, Switch } from 'react-router-dom';
import Home from './routes/home';
import MyPage from './routes/mypage';
// Headerコンポーネントを取り除き、ルーティングを設定
const App = () => (
  <BrowserRouter>
    <AuthProvider>
      <Header />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/mypage" component={MyPage} />
      </Switch>
    </AuthProvider>
  </BrowserRouter>
);
...

続いて、プライベートなルートを作成するための、PrivateRouteコンポーネントを作成します。

// index.js
...
// プライベートなルートを設定するためのコンポーネント
const PrivateRoute = ({ component: Component, ...rest }) => (
  <AuthConsumer>
    {({ isAuth }) => (
      <Route
        render={props =>
          isAuth ? (
            <Component {...props} />
          ) : (
            <Redirect to="/" />
          )
        }
        {...rest}
      />
    )}
  </AuthConsumer>
);
// 秘匿したいルートをPrivateRouteで置き換える
const App = () => (
  <BrowserRouter>
    <AuthProvider>
      <Header />
      <Switch>
        <Route exact path="/" component={Home} />
        <PrivateRoute path="/mypage" component={MyPage} />
      </Switch>
    </AuthProvider>
  </BrowserRouter>
);
...

未ログイン状態でmypageにアクセスすると、トップページにリダイレクトされることが確認できると思います。

大規模なアプリケーションなど厳格な状態管理が必要とされるケースでは、Redux等の状態管理フレームワークが引き続き有用に思いますが、中小規模のアプリケーションにおいてはContextを用いるのも良い選択肢ではないでしょうか。

参照サイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む