OTOBANK Engineering Blog

オトバンクはコンテンツが大好きなエンジニアを募集しています!

Industry Tech Kaigi (4/27) の仲間に入れてもらってイベントやります!

@riaf です。

4/27 (木) に開催される#02 Industry Tech Kaigi「巨大産業をテクノロジーでハックせよ!」 というイベントにオトバンクも一緒に登壇することになりました。

公開時にちょっと面白いことになっていたので、シェアして遊んだりしていましたが、

現在はちゃんと修正されている通り、各社の開発部門の責任者が集まるパネルディスカッションをやるはずです。

詳細はイベントページをご覧いただければと思いますが、

industry-tech-kaigi.connpass.com

普段は共通点がなさすぎて集まることのないメンバーが集まりますので、面白くなるのかどうかも検討つきませんが、いつもとは違うお話ができると良いなあなどと思っております。

「こんな話が聞きたい!」といったリクエストは @frkout まで送ってください!

Symfony2で利用されているDoctrineに入門する(中編)

こんにちわ!@mrtryです。 もう3月ですね。新卒入社して、もうすぐ1年経ちます。 新卒と言えなくなってしまうのが、ちょっと寂しい今日このごろです。

さて、「Symfony2入門」の7回目の記事です。 前回のSymfony2で利用されているDoctrineに入門する(前編)に引き続き、今日は中編をお送りします。

前回は、Doctrineについてざっくり紹介しました。 今回は、DoctrineとDBを連携するための設定 エンティティの作成 エンティティを元したテーブル作成 を紹介したいと思います。

プロジェクトをつくる

まず、準備として、Symfonyのプロジェクトをつくります。 今回はtry-doctrineという名前で作成します。

$ symfony new try-doctrine 2.8

 Downloading Symfony...

    5.29 MB/5.29 MB ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  100%

 Preparing project...

 ✔  Symfony 2.8.15 was successfully installed. Now you can:

    * Change your current directory to /Users/symmt/Program/Study/try-doctrine

    * Configure your application in app/config/parameters.yml file.

    * Run your application:
        1. Execute the php app/console server:run command.
        2. Browse to the http://localhost:8000 URL.

    * Read the documentation at http://symfony.com/doc

RDBの設定

エンティティを生成する前に、DBの設定を行います。 今回は、RDBとしてmysqlを用いて実例を紹介します。

まず、app/config/parameters.ymlにて、設定を行います。 このファイルは、Symfonyのインフラに関係する設定をするためのファイルです。 各Bundleの設定や、APIのURLなどの定数の管理などに利用されます。

プロジェクトを立ち上げた直後のparameters.ymlにはDBとメールサーバーの設定が記述されています。 今回はdatabase_nameの箇所をtry-doctrineと書き換えたものを利用します。

$ cat -n app/config/parameters.yml
  1 # This file is auto-generated during the composer install
  2 parameters:
  3     database_host: 127.0.0.1
  4     database_port: null
  5     database_name: try-doctrine
  6     database_user: root
  7     database_password: null
  8     mailer_transport: smtp
  9     mailer_host: 127.0.0.1
 10     mailer_user: null
 11     mailer_password: null
 12     secret: 328b4f9d66009015aa9e8770eb73163d0b74684f

設定が終わったら、doctrine:database:createコマンドを実行します。 実行すると、database_nameで設定した名前でDBが新しく作成されます。

$ app/console doctrine:database:create
Created database `try-doctrine` for connection named default

実際に一覧を見てみると、DBが作成されていることが確認できます。

mysql> show databases;
+---------------------------+
| Database                  |
+---------------------------+
| ...                       |
| try-doctrine              |
+---------------------------+
11 rows in set (0.01 sec)

エンティティを生成する

次に、エンティティを生成します。 doctrine:generate:entityというコマンドを利用します。 実行すると対話形式となり、一通り入力が完了すると、エンティティが生成されます。

今回は例として、Productというエンティティを生成したいと思います。 フィールド名等については、以下の表をご確認ください。

フィールド名 null許容 主キー
id id false true
name string(255) false false
price integer false false
description text false false

実際に生成する

上記の表に沿って、エンティティを生成した際の様子です。

$ app/console doctrine:generate:entity


  Welcome to the Doctrine2 entity generator



This command helps you generate Doctrine2 entities.

First, you need to give the entity name you want to generate.
You must use the shortcut notation like AcmeBlogBundle:Post.

The Entity shortcut name: AppBundle:Product

Determine the format to use for the mapping information.

Configuration format (yml, xml, php, or annotation) [annotation]:

Instead of starting with a blank entity, you can add some fields now.
Note that the primary key will be added automatically (named id).

Available types: array, simple_array, json_array, object,
boolean, integer, smallint, bigint, string, text, datetime, datetimetz,
date, time, decimal, float, binary, blob, guid.

New field name (press <return> to stop adding fields): name
Field type [string]:
Field length [255]:
Is nullable [false]:
Unique [false]:

New field name (press <return> to stop adding fields): price
Field type [string]: integer
Is nullable [false]:
Unique [false]:

New field name (press <return> to stop adding fields): description
Field type [string]: text
Is nullable [false]:
Unique [false]:

New field name (press <return> to stop adding fields):


  Entity generation


  created ./src/AppBundle/Entity/Product.php
> Generating entity class src/AppBundle/Entity/Product.php: OK!
> Generating repository class src/AppBundle/Repository/ProductRepository.php: OK!


  Everything is OK! Now get to work :).

各項目について

First, you need to give the entity name you want to generate.
You must use the shortcut notation like AcmeBlogBundle:Post.

The Entity shortcut name: AppBundle:Product

生成するエンティティ名とそれを置くBundle先をBundle名:エンティティ名という書き方で指定します。 今回はAppBundleProductというエンティティを生成するので、AppBundle:Productと書きました。

Determine the format to use for the mapping information.

Configuration format (yml, xml, php, or annotation) [annotation]:

マッピング定義のフォーマットを指定します。 マッピング定義とはアプリケーション内のクラス、プロパティと、DBのテーブル、カラムの対応関係を定義しているものです。 ざっくりなイメージとしては、 ER図の内容を設定ファイルとしたものといった感じです。 デフォルトでは、annotationで設定するようになっています。 今回はannotationでよかったので、そのままReturnで進めました。

Instead of starting with a blank entity, you can add some fields now.
Note that the primary key will be added automatically (named id).

IDの項目はDoctrine側で自動的に生成されます。 今回で言うと、idがそれにあたります。

Available types: array, simple_array, json_array, object,
boolean, integer, smallint, bigint, string, text, datetime, datetimetz,
date, time, decimal, float, binary, blob, guid.

New field name (press <return> to stop adding fields): name
Field type [string]:
Field length [255]:
Is nullable [false]:
Unique [false]:

New field name (press <return> to stop adding fields): price
Field type [string]: integer
Is nullable [false]:
Unique [false]:

New field name (press <return> to stop adding fields): description
Field type [string]: text
Is nullable [false]:
Unique [false]:

フィールド名とその型を設定していきます。 型は、Available typesにて列挙されているものを指定できます。

フィールド名 null許容 ユニークキー制約
name string false false
price integer false false
description text false false

入力が終わったら、New field name (press <return> to stop adding fields)と書いてあるとおり、フィールド名を入力せずReturnを押すと、次に進みます。

  Entity generation


  created ./src/AppBundle/Entity/Product.php
> Generating entity class src/AppBundle/Entity/Product.php: OK!
> Generating repository class src/AppBundle/Repository/ProductRepository.php: OK!


  Everything is OK! Now get to work :).

ここまで終えると、エンティティとリポジトリが生成されます。 生成されたエンティティは以下のようになります。 先程設定していたフィールドがプロパティとして書かれ、プロパティの上部には設定した型などの情報がannotationで書かれていることが確認できます。 (突然出てきたリポジトリですが、今回は置いときます)

$ cat -n src/AppBundle/Entity/Product.php
  1  <?php
  2
  3  namespace AppBundle\Entity;
  4
  5  use Doctrine\ORM\Mapping as ORM;
  6
  7  /**
  8   * Product
  9   *
 10   * @ORM\Table(name="product")
 11   * @ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository")
 12   */
 13  class Product
 14  {
 15      /**
 16       * @var int
 17       *
 18       * @ORM\Column(name="id", type="integer")
 19       * @ORM\Id
 20       * @ORM\GeneratedValue(strategy="AUTO")
 21       */
 22      private $id;
 23
 24      /**
 25       * @var string
 26       *
 27       * @ORM\Column(name="name", type="string", length=255)
 28       */
 29      private $name;
 30
 31      /**
 32       * @var int
 33       *
 34       * @ORM\Column(name="price", type="integer")
 35       */
 36      private $price;
 37
 38      /**
 39       * @var string
 40       *
 41       * @ORM\Column(name="description", type="text")
 42       */
 43      private $description;
 44
 45
 46      /**
 47       * Get id
 48       *
 49       * @return integer
 50       */
 51      public function getId()
 52      {
 53          return $this->id;
 54      }
 55
 56      /**
 57       * Set name
 58       *
 59       * @param string $name
 60       * @return Product
 61       */
 62      public function setName($name)
 63      {
 64          $this->name = $name;
 65
 66          return $this;
 67      }
 68
 69      /**
 70       * Get name
 71       *
 72       * @return string
 73       */
 74      public function getName()
 75      {
 76          return $this->name;
 77      }
 78
 79      /**
 80       * Set price
 81       *
 82       * @param integer $price
 83       * @return Product
 84       */
 85      public function setPrice($price)
 86      {
 87          $this->price = $price;
 88
 89          return $this;
 90      }
 91
 92      /**
 93       * Get price
 94       *
 95       * @return integer
 96       */
 97      public function getPrice()
 98      {
 99          return $this->price;
100      }
101
102      /**
103       * Set description
104       *
105       * @param string $description
106       * @return Product
107       */
108      public function setDescription($description)
109      {
110          $this->description = $description;
111
112          return $this;
113      }
114
115      /**
116       * Get description
117       *
118       * @return string
119       */
120      public function getDescription()
121      {
122          return $this->description;
123      }
124  }

生成したエンティティをDBに反映する

Productエンティティを作成しましたが、それに対応するproductテーブルはまだDBに存在していません。 Productエンティティに対応するproductテーブルを作成する必要があります。

既に作成されているエンティティを元にして、DBのスキーマを更新するというdoctrine:schema:updateコマンドがあります。 これを実行して、Productエンティティに対応するproductテーブルを生成します。

app/console doctrine:schema:update
ATTENTION: This operation should not be executed in a production environment.
           Use the incremental update to detect changes during development and use
           the SQL DDL provided to manually update your database in production.

The Schema-Tool would execute "1" queries to update the database.
Please run the operation by passing one - or both - of the following options:
    doctrine:schema:update --force to execute the command
    doctrine:schema:update --dump-sql to dump the SQL statements to the screen

doctrine:schema:update --dump-sql を実行すると、実際に実行されるSQLを確認することができます。 doctrine:schema:update --force を実行すると、--dump-sqlで確認したSQLが実行されます。

実際にやっていきます。 try-doctrineに存在するテーブルを確認します。 まだ何も作成していないので、Emptyになっています。

mysql> show tables;
Empty set (0.00 sec)

doctrine:schema:update --dump-sqlを実行します。 先程生成したエンティティと同等の内容のテーブルを生成するSQLが発行されています。 doctrine:schema:update --forceを実行すると、このSQLが実行されることになります。

$ app/console doctrine:schema:update --dump-sql
CREATE TABLE product (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, price INT NOT NULL, description LONGTEXT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;

実際に実行されるSQLを確認したところで、doctrine:schema:update --forceを実行して、テーブルを作成します。

app/console doctrine:schema:update --force
Updating database schema...
Database schema updated successfully! "1" queries were executed

try-doctrineに存在するテーブルを確認します。 productテーブルが新規に作成され、フィールド名等の設定もエンティティの設定と同等なものになっています。

mysql> show tables;
+------------------------+
| Tables_in_try-doctrine |
+------------------------+
| product                |
+------------------------+
1 row in set (0.00 sec)

mysql> show columns from product;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| name        | varchar(255) | NO   |     | NULL    |                |
| price       | int(11)      | NO   |     | NULL    |                |
| description | longtext     | NO   |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

これで、PHPでDoctrineを介してProductエンティティを操作することで、DBを操作する準備が整いました。

おわりに

今回は DoctrineとDBを連携するための設定 エンティティの作成 エンティティを元したテーブル作成 を紹介しました。 次回は、DoctrineでCRUDをする具体例を紹介したいと思います。

また、この記事は、@mrtryの勉強の一環で書いていますので、
お気づきの点などがありましたら、コメント等でご指摘いただければ幸いです!

参考

音声認識で文字起こしをしてみた

こんにちわ。@mrtryです。
今月の頭に、地元に帰る用事があり、先月のブログで書いたまつりやという回転寿司にいってきました。
いっぱい食べました。めっちゃ美味しかったです…!

f:id:symmt9302:20170227105300j:plain

さて今回は、先日社内LT(Teck Talk)で発表した内容を記事にしてみました。
音声認識を利用して、取材で録音した音声を自動的に文字起こしすることはできるのかというテーマで発表しました。

この話の背景

ある日、弊社の新刊JPの記者さんと飲んでいた時のことです。
記者さんが、「取材をするとき、基本ICレコーダーで録音しておいて、あとで聞きながら文字に起こしたりするんだけど、結構面倒だし、外注したりもするんだよね。プログラムでいい感じにならないですか?」と言われました。
その話を聞いて、「音声認識でイケそうな気がしますね〜。やってみますね〜。」と答えたので、ちょっと試してみました。

音声認識とは

そもそも、音声認識とは何でしょう?
とりあえずwikipediaを見てみると、以下のような説明があります。

音声認識(おんせいにんしき、英: speech recognition)とは、人間の声などをコンピューターに認識させることであり、話し言葉を文字列に変換したり、あるいは音声の特徴をとらえて声を出している人を識別する機能を指す

wikipedia: 音声認識

こんな感じで、音声認識という言葉は、声などをコンピュータに認識させるもの全般を指しています。
具体的には、

  • 音声から文字を起こす
  • 音声から感情を把握する
  • 複数人の音声から、それぞれの人ごとに音声を分離する

などがあります。
ちなみに、私は学生の頃に音声が歌声なのか話声なのかを識別するという研究をしていました。

今回は、会話を文字に起こす(Speech To Text)という音声認識を試してみます。

利用したもの

今回は、個人的に最近気になっていたIBM Watson Speech-to-Textで試してみたいと思います。
セットアップとか面倒なことしないで、ざっくりお試ししたい程度の気持ちだったので、Speech-to-Textのデモページを利用することにします。

なお、その時利用した設定は、以下のスクショを参照してください。

f:id:symmt9302:20170227105733p:plain

実際にやってみた

文字起こしを試す音源として、弊社で販売しているオーディオブックのサンプル音源テープ起こし入門サイトの練習音源で試してみます。 なお、デモページのファイル形式の都合上、元の音源はmp3からwavに変換し、サンプリングレートを16kHzにして書き出したものを利用しています。

オーディオブックのサンプル音源で試す

影響力の武器 第三版: なぜ、人は動かされるのかのページにあるサンプル音源でやってみたいと思います。 (ちなみにこの作品は、私が買ったオーディオブックで一番聴いてる作品です)

以下、実際に出力されたテキストです。 気になった方は実際の音声と、出力されたテキストを見比べてみてください。

前にも少し述べましたが他の全ての影響力の武器と同じように。社会的証明の原理にも。その効果が強くなるいくつかの条件があります。
その一つが不確かさです。
同振る舞いがいいのか確信が持てない場合に。人が普段よりいっそう他者の行動を参考にして自分の行動を決めるようになるのは間違いありません。
それに加えてもう一つ重要な条件があります。類似性です。
社会的証明の原理は自分と似ている人の行動を見ているときに最も強く最後します。
同振る舞いばいいのかを考えるとき。一番参考になるのは自分と類似した他者の行動です。
したがって私たちは自分と異なる人よりも類似した人が示す行動に従いやすいのです。
近頃のテレビではどこにでもいそうな普通の人が商品を推奨するコマーシャルがよく流れていますが。その理由もここにあると思います。
広告主は商品の最も大きな潜在的市場を形成している普通の視聴者に売り込むには他の普通の人々が。それを好んで使っていることを示せばいいとわかっているのです。
ジュースであれ鎮痛剤であれ洗剤であれどこにでもいる平均的な人々の絶賛に人は耳を傾けるものなのです。
人が他者の行動を真似るかどうかを決定するときに。その他社との類似性が重要であることを示すもっと強力な証拠がいくつかの科学的研究によって発見されています。
とりわけわかりやすい例は。ある大学のキャンパスで行われた募金活動の取り組みについての研究結果です。
募金を呼びかける人が私もここの学生ですと言って。学生たちと似た人間であることを主張しそうである以上学生たちもこの同じ目標を後押ししたくなって当然だと暗にほのめかして時にはそうしなかったときと比べてチャリティーへの寄付が二倍以上も集まりました。
この結果は社会的証明の原理の重要な条件を示しています。
私たちは他の人の行動から自分にとっての適切な行動を決定しますが。そうした傾向がとりわけ強まるのはそのほかの人が自分と似ている場合などです。

元の音声を確認するまでもなく、テキストを見るだけで概ねキレイに起こされていますね。
オーディオブックの音声は、音声以外の余計な音(要はノイズ)がなく、文字起こしするには理想的な音声なので、キレイに起こすことができたのではないか、と思いました。

文字起こし入門サイトの練習音源で試す

今度は、実際の文字起こしの依頼で来るであろう音声の例として、以下のテープ起こし入門サイトの練習音源で試してみます。

以下、実際に出力されたテキストです。 気になった方は実際の音声と、出力されたテキストを見比べてみてください。

ヘッドも映画の中に挟まれてきてビジュアル化は持ってくるわけですから法を右折そういうの一千万画素。
アミノ酸は新しい情報を信じて下さい。
うん遊びをお客さん販促よ。
話だよ遊ぶことができるします。
MRFからちょっと離れている&会社がすぐ。
ええには英語欲しいとかいる政策が発足の。
笑知れたらすぐもうほんとね三行ユニフォーム出雲反射の演出もあることができたらすぐに自分。
株なんですか。
うん光を。
でも私も半分です。

先程のオーディオブックで試したテキストと比べると、なかなか、意味が読み取れないテキストになってしまいました。 音源を載せていたサイトに、音声から起こしたテキストがあるので、比較してみます

皆さん方もね、だんだんだんだん年齢重ねてきて、あのー、20代がこれからはね、追ってくるわけですから、多分ぼーっとしていると、そういう元気のいい人にばっといきなり抜かれちゃうんで、とにかくね、あの、新しい情報を仕入れてください。で、あの、それをお客さんに返すことによって、またお客さんから信頼を勝ち取ることができるかもしれません。特に、あのー、インターネットからちょっと離れている、あの、アナログな会社なんかは、すごく狙い目でね。で、皆さん、今、テープ起こしとかWEB制作とか、何か一つの職種でやってらっしゃると思うんですけど、もうちょっと何か目先を広げて、複合的にその会社の、えー、力になることができたら素晴らしいと思うんです。それでまず、あのー、いっ……何て言うんですか、えー、きっかけをね、得てほしい。どっかからでもいいです。

テープ起こし はじめの一歩:第6週 逐語記録見本: テープ起こし はじめの一歩

声に出してみると、似てなくもなさそうな箇所があったりしますが、全然一致していませんね…。

原因としては、元の音声の品質が悪いということがあげられるかと思います。 先程のオーディオブックの音声と比べると、この音源は以下の点で品質が劣っています。

  • 定常的なノイズ(サーッって音)が乗っている
  • 部屋の反響音が強く、声が明瞭ではない
  • 音割れがある

特に、部屋の反響音が強く、声が明瞭ではないという点が問題にありそうです。
音声には、母音や子音ごとに特有の周波数パターンがあり、それを見ることで、発声されている言葉を識別することができます。 今回の音声は、反響音が強いために、音声に余計な音が付与されてしまい、言葉を識別するのが難しくなり、意味不明な文章が生成されたのかな、と感じました。 (詳細が気になった方は、フォルマントMFCCで検索すると、幸せになれます)

取材の音声を貰ったとすると、きれいに文字起こしできるのか?

ふたつの音源で文字起こしをした結果を踏まえて、取材の音声を文字起こしできるかを考えました。 以下の条件を満たしていれば、おそらく良い感じに文字起こしすることができるのではないか、と感じました。

  • 外部ノイズ(環境音、BGM)が少ない
  • 音声が明瞭に録音されている
  • 発話者の滑舌がある程度キレイである

おわりに

今回は、IBM Watson Speech-to-Textを用いて、音声の文字起こしをしてみました。 音声の品質に依るところはありますが、録音時点から気を使うことができれば、いい感じに自動で文字起こしができそうなことがわかりました。 機会があれば、記者の方と連携して、実際の取材音声で文字起こしを試してみたいな、と思っています。 その際は、またブログの記事にして、紹介してみたいと思います!