読者です 読者をやめる 読者になる 読者になる

『ゼロから作る Deep Learning』を読んだ

book

AlphaGo が碁の元世界王者イ・セドル氏に勝利したり、 IBM のワトソンが難症例患者の正しい病名を見抜いたり、 Google 翻訳がニューラルネットワーク版に切り替わって翻訳精度が劇的に向上したりと、AI が急激に身近になった昨今。

今さらながら AI、とりわけ Deep Learning について知っておきたいと思って購入。 今年買った技術書で一番勉強になった。

Deep Learning を扱った本は雨後のタケノコのようにたくさん出版されたけど、書店でパラパラっと眺めて、難しい数式に拒否反応が出て棚に戻すものが殆どだった。高度なコンピュータサイエンスを学んでいないこの身がツライ。

その点、本書は Python のコードが中心だったので心理的障壁が低かった。 そんな本書でも数式は避けられないので当然出てくるが、計算グラフを使った解説のおかげで、自分みたいな数式アレルギーでもなんとか理解できた。

技術の仕組みについて勉強するなら、実際に作ってみるのが一番。 本書では、Python と NumPy を使って Deep Learning を実装していく。 パーセプトロンに始まり、 ニューラルネットワーク、 畳み込みニューラルネットワーク、 そして Deep Learning へと、 段階的に進んでいく。 Deep Learning が突然生まれた技術ではなく、過去から続く研究がビッグデータとマシンパワーで花開いた、というのが良くわかる。

紙面の都合なのか、後半はソースコードの省略が結構あるので、サポートサイトからソースコードをダウンロードするのは必須。

一度読み終えただけでは、とても理解できたとは言いがたいので、 何度も読み直すつもり。 それにしても、自分でもゼロから Deep Learning が作れそうに思えてくるから不思議だ。 それくらい本書が良書なわけだが。 今度は Python ではなく C# で実装しながら読んでみたいと思う。

『松坂牛 100% 黄金のハンバーグ』を食べた

gourmet

前から気になっていて、いつか食べたいと思っていた『松坂牛 100% 黄金のハンバーグ』を、ついに注文してしまった。 送料が 1000 円以上かかるから躊躇していたんだけど、楽天ポイントが送料分くらいたまっていたのでポチッと。

最初、勘違いして冷蔵庫で1晩解凍しようとしていたので、危機一髪。 間違ってステーキの説明を読んでいたみたいだ。 冷凍のまま焼けるので、着いたその日にいただくことにした。

両面に焼き目をつけたら、水を入れて蒸し焼きにする。 ちゃんと説明書を読めば難しくない。 焼き上がりはこんな感じ。

柔らかいだけでなく、肉肉しさもあって、今までに食べたことがないハンバーグだった。 味がしっかりついていて、ソースは不要。 それも、肉の味・旨みが凝縮されたような味わい。 白米との相性も格別。 リピーターが多いというのも頷ける。 送料がネックだけど、また注文したいと思える一品だった。


御○屋

gourmet

昼休みにちょっと足を伸ばして、ビックカメラ1号館横にある『御○屋』に行ってみた。カウンター席が空いていたので、直ぐに座れてラッキー。並んでたら、昼休み中に会社に戻れなかったかもしれない。

ランチのつけ麺セットを注文。プラス120円で大盛りにするか悩んだけど、初めての店なので止めておいた。

麺はツルシコ。明太子で和えたもやしが美味い。

つけ汁は魚介の味がしっかりしていて、そしてピリ辛で美味。そして、予想していたよりも濃厚だった。

ランチは白米か焼豚おにぎりが選べたので、焼豚おにぎりを選択。昼はやっぱりお米が食べたくなる。

デザートに杏仁豆腐まで付いていて、コスパは結構良いのでは。杏仁豆腐好きなので嬉しい。

期待以上の味で、昼休み中に会社に戻れないかもしれないリスクを取ってまで足を運んだ甲斐があった。ランチは担々麺のセットもやっているんで、今度は担々麺も食べてみたいと思えた。

関連ランキング:居酒屋 | 天神南駅西鉄福岡駅(天神)渡辺通駅

VirtualBox でゲスト OS からホスト OS にアクセスするときに指定する IP アドレス

tool

VirtualBox のゲスト OS からホスト OS にアクセスしたいことが結構ある。 例えば、ゲスト OS から、ホスト OS でデバッグ実行している Web API にアクセスしたいときとか。

必要になるたびにネットで調べているので、ブログにメモしておく。

ネットワークアダプタの方式 ホスト OS の IP アドレス
NAT 10.0.2.2
ホストオンリーネットワーク 192.168.56.1

redux-form で表形式入力的なフォーム

javascript

redux-form の FieldArray を使えば、 複数のデータを同時に編集して一括登録、 みたいなことができる。 表形式入力的なフォーム。 DataGrid もどき。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers } from 'redux';
import { Provider, connect } from 'react-redux';
import {
  Field,
  FieldArray,
  reduxForm,
  reducer as formReducer
} from 'redux-form';
import {
  Grid,
  Row,
  Col,
  Button,
  Table,
  FormGroup,
  ControlLabel,
  FormControl
} from 'react-bootstrap';

// bootstrap の CSS を読み込む。
// webpack で CSS Module を使っていることが前提。
import '../node_modules/bootstrap/dist/css/bootstrap.css';

// カスタマーを複数追加するアクション
const ADD_CUSTOMERS = "ADD_CUSTOMERS";

// カスタマーを複数追加するときに使う Action Creator
function addCustomers(customers) {
  return { type: ADD_CUSTOMERS, customers };
}

// 初期状態
const initialState = {
  customers: [
    { code: "10", name: "香川", kana: "カガワ" },
    { code: "4", name: "本田", kana: "ホンダ" },
    { code: "5", name: "長友", kana: "ナガトモ" },
    { code: "9", name: "岡崎", kana: "オカザキ" }
  ]
};

// カスタマーを操作する Reducer
const customersReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_CUSTOMERS:
      return Object.assign({}, state, {
        customers: [...state.customers, ...action.customers]
      });
    default:
      return state;
  }
};

// redux-form が提供する reducer も使う
const rootReducer = combineReducers({
  customers: customersReducer,
  form: formReducer
});

// アプリ唯一のストアを作成
const store = createStore(rootReducer);

// redux-form 用バリデーション関数
// customers の中身を forEach で回して 1 つずつチェックする。
const validate = (values) => {
  const errors = {};
  if (!values.customers || values.customers.length == 0) {
    errors.customers = { _error: "カスタマーを入力してください。" };
  } else {
    const customersArrayErrors = [];
    values.customers.forEach((customer, index) => {
      const customerErros = {};
      if (customer && !customer.code) {
        customerErros.code = "コードを入力してください。";
      }
      if (customer && !customer.name) {
        customerErros.name = "名前を入力してください。";
      }
      if (customer && !customer.kana) {
        customerErros.kana = "カナを入力してください。";
      }
      customersArrayErrors[index] = customerErros;
    });
    if (0 < customersArrayErrors.length) {
      errors.customers = customersArrayErrors;
    }
  }
  return errors;
};

// フォーム入力用コンポーネント
const renderField = ({ input, type, meta: { touched, error } }) => (
  <td>
    <FormControl {...input} type={type}/>
    {touched && error && <span className="text-danger">{error}</span>}
  </td>
);

// カスタマー表形式入力用コンポーネント
// FieldArray の component に渡したコンポーネントには
// props に fileds が渡ってくる。
const renderCustomers = ({ fields, meta: { touched, error } }) => (
  <Table>
    <thead>
      <tr>
        <th>コード</th>
        <th>名前</th>
        <th>カナ</th>
      </tr>
    </thead>
    <tbody>
      {fields.map((customer, index) =>
        <tr key={index}>
          <Field
            name={`${customer}.code`}
            type="text"
            component={renderField}/>
          <Field
            name={`${customer}.name`}
            type="text"
            component={renderField}/>
          <Field
            name={`${customer}.kana`}
            type="text"
            component={renderField}/>
        </tr>
      )}

      {
        touched && error &&
        <tr>
          <td><span className="text-danger">{error}</span></td>
          <td></td>
          <td></td>
        </tr>
      }

      <tr>
        <td>
          <Button type="button" onClick={() => fields.push()}>
            行追加
          </Button>
        </td>
        <td></td>
        <td></td>
      </tr>
    </tbody>
  </Table>
);

// カスタマー登録フォーム本体
class _CustomerTableForm extends Component {
  render() {
    const {
      handleSubmit,
      pristine,
      reset,
      submitting
    } = this.props;

    return (
      <form onSubmit={handleSubmit}>
        <FieldArray name="customers" component={renderCustomers} />

        <FormGroup>
          <Button bsStyle="primary"
                  type="submit"
                  disabled={submitting}>
            保存
          </Button> 
          <Button type="button"
                  disabled={pristine || submitting}
                  onClick={reset}>
            リセット
          </Button>
        </FormGroup>
      </form>
    );
  }
}

// カスタマー登録フォームを redux-form 化。
// form にはユニークな文字列を指定する。
const CustomerTableForm = reduxForm({
  form: "customerForm",
  validate
})(_CustomerTableForm);

// さらに connect でコンテナコンポーネント化。
// redux-form でラップしたコンポーネントには onSubmit の props が
// 生えている。
// submit 時の処理はコンテナコンポーネントで実装する。
const CustomerTableFormContainer = connect(
  state => {
    return {};
  },
  dispatch => {
    return {
      onSubmit: (values) => {
        dispatch(addCustomers(values.customers));
      }
    };
  }
)(CustomerTableForm);

// カスタマー一覧を表示するコンポーネント
class CustomerList extends Component {
  renderCustomers() {
    return this.props.customers.map(c =>
      <tr key={c.code}>
        <td>{c.code}</td>
        <td>{c.name}</td>
        <td>{c.kana}</td>
      </tr>
    );
  }

  render() {
    return (
      <Table>
        <thead>
          <tr>
            <th>コード</th>
            <th>名前</th>
            <th>カナ</th>
          </tr>
        </thead>
        <tbody>
          {this.renderCustomers()}
        </tbody>
      </Table>
    );
  }
}

const CustomerListContainer = connect(
  state => {
    return { customers: state.customers.customers };
  }
)(CustomerList);

class App extends Component {
  render() {
    return (
      <Grid>
        <Row>
          <Col md={12}>
            <CustomerTableFormContainer />
            <CustomerListContainer />
          </Col>
        </Row>
      </Grid>
    );
  }
}

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("app")
);

だんだん、バリデーションのための関数を書くのがメンドクなってきた。

redux-form でも react-bootstrap のフォーム関連コンポーネントを使う

javascript

試行錯誤の末に、 react-bootstrap が提供する FormGroupFormControl といったフォーム関連コンポーネントを、 redux-form と一緒に使うことができた。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers } from 'redux';
import { Provider, connect } from 'react-redux';
import {
  Field,
  reduxForm,
  reducer as formReducer
} from 'redux-form';
import {
  Grid,
  Row,
  Col,
  Table,
  FormGroup,
  ControlLabel,
  FormControl
} from 'react-bootstrap';

// bootstrap の CSS を読み込む。
// webpack で CSS Module を使っていることが前提。
import '../node_modules/bootstrap/dist/css/bootstrap.css';

// カスタマーを追加するアクション
const ADD_CUSTOMER = "ADD_CUSTOMER";

// カスタマーを追加するときに使う Action Creator
function addCustomer(customer) {
  return { type: ADD_CUSTOMER, customer };
}

// 初期状態
const initialState = {
  customers: [
    { code: "10", name: "香川", kana: "カガワ" },
    { code: "4", name: "本田", kana: "ホンダ" },
    { code: "5", name: "長友", kana: "ナガトモ" },
    { code: "9", name: "岡崎", kana: "オカザキ" }
  ]
};

// カスタマーを操作する Reducer
const customersReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_CUSTOMER:
      return Object.assign({}, state, {
        customers: [...state.customers, action.customer]
      });
    default:
      return state;
  }
};

// redux-form が提供する reducer も使う
const rootReducer = combineReducers({
  customers: customersReducer,
  form: formReducer
});

// アプリ唯一のストアを作成
const store = createStore(rootReducer);

// redux-form 用バリデーション関数
const validate = (values) => {
  // 検証に失敗したら values と同じキーで
  // エラーメッセージをセットする。
  const errors = {};
  if (!values.code) {
    errors.code = "コードを入力してください。";
  }
  if (!values.name) {
    errors.name = "名前を入力してください。";
  }
  if (!values.kana) {
    errors.kana = "カナを入力してください。";
  }
  return errors;
};

// フォームの入力用コンポーネント。
// react-bootstrap のフォーム関連コンポーネントを使う。
class FormField extends Component {
  render() {
    // input と meta は redux-form が渡してくる props。
    // type や label は Field に渡した props がそのまま渡ってくる。
    // select や textarea に対応するために componentClass を受け取る。
    // select の option に対応するために children も受け取る。
    const {
      input,
      label,
      type,
      componentClass,
      children,
      meta: {
        touched,
        error
      }
    } = this.props;

    return (
      <FormGroup>
        <ControlLabel>
          {label}
        </ControlLabel>

        <FormControl
          {...input}
          type={type || "text"}
          componentClass={componentClass || "input"}
          >
          {children}
        </FormControl>

        {touched && error && <span className="text-danger">{error}</span>}
      </FormGroup>
    );
  }
}

// カスタマー登録フォーム本体
class _CustomerForm extends Component {
  render() {
    const {
      handleSubmit,
      pristine,
      reset,
      submitting
    } = this.props;

    return (
      <form onSubmit={handleSubmit}>
        <Field name="code"
               component={FormField}
               type="text"
               label="コード"/>
        <Field name="name"
               component={FormField}
               type="text"
               label="名前"/>
        <Field name="kana"
               component={FormField}
               type="text"
               label="カナ"/>

        <div className="form-group">
          <button className="btn btn-primary"
                  type="submit"
                  disabled={submitting}>
            追加
          </button> 
          <button className="btn btn-default"
                  type="button"
                  onClick={reset}
                  disabled={submitting || pristine}>
            クリア
          </button>
        </div>
      </form>
    );
  }
}

// カスタマー登録フォームを redux-form 化。
// form にはユニークな文字列を指定する。
const CustomerForm = reduxForm({
  form: "customerForm",
  validate
})(_CustomerForm);

// さらに connect でコンテナコンポーネント化。
// redux-form でラップしたコンポーネントには onSubmit の props が
// 生えている。
// submit 時の処理はコンテナコンポーネントで実装する。
const CustomerFormContainer = connect(
  state => {
    return {};
  },
  dispatch => {
    return {
      onSubmit: (values) => {
        dispatch(addCustomer(values));
      }
    };
  }
)(CustomerForm);

// カスタマー一覧を表示するコンポーネント
class CustomerList extends Component {
  renderCustomers() {
    return this.props.customers.map(c =>
      <tr key={c.code}>
        <td>{c.code}</td>
        <td>{c.name}</td>
        <td>{c.kana}</td>
      </tr>
    );
  }

  render() {
    return (
      <Table>
        <thead>
          <tr>
            <th>コード</th>
            <th>名前</th>
            <th>カナ</th>
          </tr>
        </thead>
        <tbody>
          {this.renderCustomers()}
        </tbody>
      </Table>
    );
  }
}

const CustomerListContainer = connect(
  state => {
    return { customers: state.customers.customers };
  }
)(CustomerList);

class App extends Component {
  render() {
    return (
      <Grid>
        <Row>
          <Col md={12}>
            <CustomerFormContainer />
            <CustomerListContainer />
          </Col>
        </Row>
      </Grid>
    );
  }
}

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("app")
);

redux-form の Field は自身に渡された props を、component に指定したコンポーネントにほとんど渡してくれる。なので FormControl が必要とするものを Field に渡せばいいって寸法。

ライブラリのソースコードを読む習慣は大事だね。

redux-form 慣れてきた

javascript

React でフォームを実装するためのライブラリは、決定版と呼べるものが見当たらないので、一番スターが多い redux-form を使っている。

github.com

フォームの入力内容をストアに持つのはいかがなものかと思っていたのに、不思議と使っているうちに気にならなくなってきた。あと、v5 までは API がツラくて、何度も使うのをやめたくなったけど、v6 で API が刷新されてだいぶマシになった。

基本的にシンプルなフォームなら下記のように書ける。 onChange を書きまくらなくていいだけでも十分助かる。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers } from 'redux';
import { Provider, connect } from 'react-redux';
import {
  Field,
  reduxForm,
  reducer as formReducer
} from 'redux-form';

// カスタマーを追加するアクション
const ADD_CUSTOMER = "ADD_CUSTOMER";

// カスタマーを追加するときに使う Action Creator
function addCustomer(customer) {
  return { type: ADD_CUSTOMER, customer };
}

// 初期状態
const initialState = {
  customers: [
    { code: "10", name: "香川", kana: "カガワ" },
    { code: "4", name: "本田", kana: "ホンダ" },
    { code: "5", name: "長友", kana: "ナガトモ" },
    { code: "9", name: "岡崎", kana: "オカザキ" }
  ]
};

// カスタマーを操作する Reducer
const customersReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_CUSTOMER:
      return Object.assign({}, state, {
        customers: [...state.customers, action.customer]
      });
    default:
      return state;
  }
};

// redux-form が提供する reducer も使う
const rootReducer = combineReducers({
  customers: customersReducer,
  form: formReducer
});

// アプリ唯一のストアを作成
const store = createStore(rootReducer);

// redux-form 用バリデーション関数
const validate = (values) => {
  // 検証に失敗したら values と同じキーで
  // エラーメッセージをセットする。
  const errors = {};
  if (!values.code) {
    errors.code = "コードを入力してください。";
  }
  if (!values.name) {
    errors.name = "名前を入力してください。";
  }
  if (!values.kana) {
    errors.kana = "カナを入力してください。";
  }
  return errors;
};

// フォームの入力用コンポーネント。
// input と meta は redux-form が渡してくる props。
// type や label は Field に渡した props がそのまま渡ってくる。
const renderField = ({ input, type, label,meta: { touched, error } }) => (
  <div className="form-group">
    <label>{label}</label>
    <input {...input} type={type} className="form-control" />
    {touched && error && <span className="text-danger">{error}</span>}
  </div>
);

// カスタマー登録フォーム本体
class _CustomerForm extends Component {
  render() {
    const {
      handleSubmit,
      pristine,
      reset,
      submitting
    } = this.props;

    return (
      <form onSubmit={handleSubmit}>
        <Field name="code"
               component={renderField}
               label="コード"/>
        <Field name="name"
               component={renderField}
               label="名前"/>
        <Field name="kana"
               component={renderField}
               label="カナ"/>

        <div className="form-group">
          <button className="btn btn-primary"
                  type="submit"
                  disabled={submitting}>
            追加
          </button> 
          <button className="btn btn-default"
                  type="button"
                  onClick={reset}
                  disabled={submitting || pristine}>
            クリア
          </button>
        </div>
      </form>
    );
  }
}

// カスタマー登録フォームを redux-form 化。
// form にはユニークな文字列を指定する。
const CustomerForm = reduxForm({
  form: "customerForm",
  validate
})(_CustomerForm);

// さらに connect でコンテナコンポーネント化。
// redux-form でラップしたコンポーネントには onSubmit の props が
// 生えている。
// submit 時の処理はコンテナコンポーネントで実装する。
const CustomerFormContainer = connect(
  state => {
    return {};
  },
  dispatch => {
    return {
      onSubmit: (values) => {
        dispatch(addCustomer(values));
      }
    };
  }
)(CustomerForm);

// カスタマー一覧を表示するコンポーネント
class CustomerList extends Component {
  renderCustomers() {
    return this.props.customers.map(c =>
      <tr key={c.code}>
        <td>{c.code}</td>
        <td>{c.name}</td>
        <td>{c.kana}</td>
      </tr>
    );
  }

  render() {
    return (
      <table>
        <thead>
          <tr>
            <th>コード</th>
            <th>名前</th>
            <th>カナ</th>
          </tr>
        </thead>
        <tbody>
          {this.renderCustomers()}
        </tbody>
      </table>
    );
  }
}

const CustomerListContainer = connect(
  state => {
    return { customers: state.customers.customers };
  }
)(CustomerList);

class App extends Component {
  render() {
    return (
      <div id="wrapper">
        <CustomerFormContainer />
        <CustomerListContainer />
      </div>
    );
  }
}

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("app")
);