「全ての知識を20字でまとめる」書評 アウトプットの時間が取れないと悩む人必読の書
「学習したことをアウトプットする癖をつければ、吸収は圧倒的に早くなる」ということは、技術職なら必ず一度は聞いたことがあるだろう。
しかし、アウトプットの有効性について理解していても、実際にはその通りにできず、インプットのみの学習に留まってしまうと感じている人は多いのではないだろうか。私もその一人だ。
今日はそのような悩みを抱える人の助けになれば良いと思い、「全ての知識を20字でまとめる」という本を紹介したい。
アウトプットするのはしんどい
世の戦闘力の高い方々はアウトプットしろアウトプットしろと言うが、そもそもインプットするだけでも大変なことだ。
会社から帰ってきて、酒を呑むのもtwitterをダラダラ眺めるのも我慢し、本を開き、疲れた脳に新しい事柄を叩き込むのは、プロとして飯を食うなら当たり前のことであっても、決して楽なことではない。
これに加えて、その内容をアウトプットするとなると、負荷は飛躍的に増大する。
「そんな時間や労力を、いったいどうしたら捻出できるのだろう?」と首を傾げながら、結局インプット偏重型の学習から脱することができない人は多いのではないだろうか。
私もこのようにブログを持っているが、更新頻度は低く、アウトプットといえばtwitterで気づきをツイートする程度に留まってしまっている。
アウトプットに時間がかかる理由
そもそもアウトプットにはなぜ時間がかかるのか。自分が業務で分析の報告資料を作成している時のことを思い出すと、自分が知っていることを他者が理解可能なように整理する作業に殆どの時間が費やされていたことに気づく。
文章や資料を作成した経験のある人なら誰しも理解していることだが、成果物を簡潔にしようとすればするほど、その整理にはより時間がかかる。
しかし時間が惜しいからと自分の頭を整理するプロセスをいい加減にすると、実作業は道標のない山道を登るがごとく迷走し、より多くの時間が失われてしまう(最悪、完成しない)。
結局アウトプット高速化の肝は、知識を整理するプロセスをいかに妥当かつ素早く行うか、という点にかかっている。
「全ての知識を20字でまとめる」
素早く知識を整理し、できるだけ少ない時間でアウトプットする術を身に着けたい。
そのようなモノグサ心課題意識を持って手にとったのが、この「全ての知識を20字でまとめる」だ。
twitterの140字制限にいつも苦しんでいる身からすると、20字という短さは驚愕だ。
しかしこの本には、様々な知識を20字でまとめるためのフレームワークが記されている
試しに、この本の内容を自分で20字程度でまとめてみると、次のようになる。
具体的問題を念頭に置けば知識の再構成は容易(21字)
20字といいつつ21字になってしまっているが、3字までは誤差として許されるようだ。
著者曰く、このような簡潔な一文を量産するスキルを磨けば、学習が定着しやすくなり、他者にとって有益なアウトプットも出せるようになるのだと言う。
3つの疑問詞を意識しながら学習する
では、どのような切り口で攻めればこのような簡潔なまとめができるようになるのだろうか?
この本では、WHY、WHAT、HOWという3つの疑問詞を意識することが鍵だと言う。
トヨタに勤めていた著者は、説明力の高いデキル上司を観察するうちに、「『分かる』とは3つの疑問詞に答えられること」だという結論を得たとのことだ。
WHY なぜ学ぶ必要があるのか?
学習者は、学習を始める前に「この学習を行うのは、どのような問題を解決するためなのか」ということを強く意識しなければならない。
何となく本を買い、何となく読み、何となく満足する。このような学習は「消費型」の学習であり、得るものは少ないと筆者は指摘する。
逆に、特定の問題を意識して本を読むことで、必要のない情報をバッサリ切り落とすことができ、20字という非常に小さな枠へとまとめることができるようになる。
WHAT 何を学んだのか?
学習の目的を明瞭に定めたら、次はその問題の解決策を本文から探る。
問題発生の背景にある真因は何なのか、それを解消する方法は何なのか。目的を念頭に置くことで、枝葉末節にとらわれずに必要な部分だけを吸収することができる。
HOW どのように活かすのか?
学習し終えたら、その学習内容を「WHY」で定めた問題に対してどのように活かしていくかを考える。
この際、抽象的な表現ではなく、動作を規定するような具体的な言葉として書き出すことが重要だという。
「すべての知識を20字でまとめる」を20字でまとめる
試しにこの本の示す方法に従って、この本自身を20字でまとめる作業を行ってみた。 この本では学習事項をまとめるためのフレームワークがいくつか示されているが、重要なポイントは下記の2つだ。
- WHY、WHAT、HOWの3つの疑問詞に対し、それぞれ2,3程度の回答を書く
- 上記の回答を元に、学んだことを20字で要約する
Markdownでまとめると下記のようになる。
- 3つの疑問詞
- WHY?
- 学んだことを高速にアウトプットしたい
- 高速なアウトプットのために、知識を素早く整理する術を身に着けたい
- WHAT?
- 具体的な問題に適用すれば、知識を再構成することは容易である
- 学ぶ目的(WHY)を設定し、その問題設定に従って内容(WHAT)を把握し、自分の具体的な動作(HOW)に落とし込むことが重要
- HOW?
- 本を読んだ際は、「3つの疑問詞」「20字まとめ」の2つのアウトプットを実践する
- 上記のアウトプットを種として、ブログに書評を書く
- WHY?
- 20字でまとめると?
- 具体的問題を念頭に置けば知識の再構成は容易
個人的な感想
20字は、実際に書いてみると分かるがあまりに短い(世界一短い詩の形式である俳句は17字)。
逆に言うと、この短さにまとめることができるということは、学習対象のエッセンスを抽出できているということが言えると思う。
また、3つの疑問詞に従って情報を整理するという手法自体は、業務で報告書を作る時等に常に意識していたことだったので意外性はなかったが、日々の学習全てに対してそのフレームワークを適用するという発想はなかった。
時間がかかりがちな知識の再整理という作業を高速で行うためのヒントは、多く得られたと感じる。
実際に20字まとめをベースにしてこの記事を書いたが、今までよりも執筆中に書き方に悩む時間はかなり減った。
(その割に自分で読んでいてこの記事の質が高くないなと思うのは、「WHY?」の掘り下げがいまいち浅かったからだと思う)
今後もこの20字まとめを実践し、インプット→アウトプットのサイクルを高速にしていきたい。
SQLのウィンドウ関数で粒度の異なるグルーピングを同時に行う
背景
SQLで分析をしていると、粒度の異なるグルーピングを同時に行いたくなる場合がある。
例えば何らかの割合を出すために「分子はカラムAとカラムBでGROUP BYしてSUM、分母はカラムBだけでGROUP BYしてSUMしたい」という場合がある。
このような場合に、教科書どおりにGROUP BY句を使うだけではうまくいかないことが多い。
この記事では、ウィンドウ関数を用いてそのような集計をパパっと行う方法を問題形式で学ぶ。
問題
設定
あなたはとあるデジタルコンテンツ販売企業のアナリストであり、このたびマンガ部門の分析を受け持つことになった。
この部門では、将来的にユーザーごとにパーソナライズされたオススメ機能を実装することを考えている
しかし今はまだそのリソースがないため、当面は「性別ごとに人気のあるカテゴリをメールでオススメする」というアナログな手法を採用することになった。
分析に使えるテーブルは販売テーブル(sales)、ユーザーマスタ(users)、商品マスタ(items)の3つで、ER図は下記の通りだ。
また、サンプルデータはGithubにあげておいたので、データに対して実際にクエリを叩きたい方はこちらをお好きなDBに入れてご利用いただきたい。
問題1
上記のような状況下で、マネージャーから下記のように依頼された。
- 性別ごとのカテゴリ別構成比を出してほしい
- 計算方法としては、性別ごとに総販売個数に占める各カテゴリの販売個数の割合を算出してほしい
- 成果物は、SQLで算出した下図左のような表と、それを元にした右図のようなグラフ
それでは、早速集計してみよう。
正しいクエリが書ければ、上記の表と同じ結果が返ってくるはずだ。
マネージャーの言った「性別ごとのカテゴリ別販売構成比」の定義は、下記のようになるだろう。
ここで、分母と分子とで集計の粒度が異なることが分かるだろうか。分子は「性別+カテゴリ」で、分母は「性別」で、それぞれグルーピングする必要がある。
GROUP BYに指定できるカラムの組み合わせは一つなので、教科書どおりにGROUP BY句を使用しても、このような集計は行うことができない。
では、どうするか。やり方にはいくつか考えられるが、最も簡潔なのがウィンドウ関数を使う方法だ。
-- 性別ごとの販売構成比 SELECT DISTINCT sex , category , COUNT(sales_id) OVER(PARTITION BY sex, category) / COUNT(sales_id) OVER(PARTITION BY sex) AS category_rate FROM ${sales_table} INNER JOIN ${user_table} USING(user_id) INNER JOIN ${item_table} USING(item_id) ORDER BY sex , category ;
このクエリのポイントは3つある。
- GROUP BY句を使わず、ウィンドウ関数を使う
- ウィンドウ関数のOVER句にはORDER BYを書かない
- SELECT句にDISTINCTを書く
一つ一つ解説する。
1. GROUP BY句を使わず、ウィンドウ関数を使う
GROUP BY句はグルーピングの粒度を1つしか指定できないが、ウィンドウ関数は一つのSELECT句の中に異なる粒度のグルーピングを共存させることができる(PARTITION BYで指定するカラムを変えれば良い)。
こうすることで、分母と分子で異なる粒度で集計をすることができる。
2. ウィンドウ関数のOVER句にはORDER BYを書かない
通常、ウィンドウ関数は累積和等の順序の概念のある計算を行うために利用される。
しかし、ウィンドウ関数のOVER句にORDER BYを書かないと、通常のGROUP BY句と同様に順序を意識しない計算をしてくれる。
つまり、次の2つのSQLは等価になる。
- ウィンドウ関数を使う場合
SELECT DISTINCT user_id , COUNT(sales_id) OVER(PARTITION BY user_id) FROM sales ;
- 通常のGROUP BY句を利用する場合
SELECT user_id , COUNT(sales_id) FROM sales GROUP BY user_id ;
3. SELECT句にDISTINCTを書く
ウィンドウ関数はGROUP BY句と異なり、レコードの集約をしない。
そのため、DISTINCTをつけないと元のレコード数と同じレコード数の結果が返ってきて冗長になる。
問題2
それでは、異なる粒度のグルーピングをもう1問やってみよう。
問題1で計算した「性別ごとのカテゴリ別販売構成比」の表とグラフを再掲すると、下記のようになる。
これを見ると、男性でも女性でも少女漫画が一番人気という風に見える。
ここで「なるほど、最近の男性の間では少女漫画が流行っているんだな」などと早合点してはいけない。
直観に反する結果が出た時は、必ずドリルダウンして本当にそうなっているのかを検証するべきだ。
確認が不十分なまま報告を行うと、後で恥をかくことも多い。
試しに、性別ごとの販売個数から一歩ドリルダウンし、ユーザーごとの販売個数を男性に関して見てみよう。
- クエリ
-- 男性のカテゴリ別販売構成比 SELECT user_id , category , COUNT(sales_id) AS sales_cnt FROM sales INNER JOIN users USING(user_id) INNER JOIN items USING(item_id) WHERE sex = ‘男’ GROUP BY user_id , category ORDER BY user_id , category ;
- 結果
これを見ると、ユーザーu00001
だけが猛烈な勢いで少女漫画を読んでいることが分かる。
そもそも他のユーザーがたかだかマンガを3~4冊しか購入しないのに対し、このユーザーだけ7冊も買っているので、集計結果に対して与える影響は極めて大きい。
この外れ値のせいで、危うく「少女漫画が男性に人気」という誤った解釈を出すところだった。
この例は極端だが、一部の並外れた行動をするユーザーによって全体の集計結果が引っ張られるというのは、分析をしているとよく起こることだ。
このことを踏まえると、現状の定義は性別ごとの各カテゴリの人気を図る指標としては不十分であると言える。
購入数の多い一部ユーザーに引っ張られないために、「ユーザーごとに販売構成比を出してから、その値を性別ごとに平均する」というアプローチを取ることにしよう。
イメージとしては、下図のようになる(値はダミー)。
例えば男性における少年漫画の販売構成比を出したい場合、男性の少年漫画の面積(青色部分)の総和を、男性ユーザーの棒グラフの総面積で割れば良い
このような方法なら、個人ごとの分母の差に結果が引きずられない。
さて、このような結果を出すクエリを書くにはどうすれば良いだろうか?
先に答えだけ掲載すると、下記のようになる。
実際に集計をしてみよう。
「ユーザーごとの集計」「性別ごとの集計」という2段階の集計を経る必要があるので、WITH句を使って段階的にクエリを書くのが良いだろう。
それぞれの段階において、異なる粒度のグルーピングのテクニックを使うことになる。
解答は下記の通りだ。
-- 性別ごとのカテゴリ別販売構成比 WITH -- ユーザーごとの販売構成比 category_rate_per_user as ( SELECT DISTINCT user_id , sex , category , COUNT(sales_id) OVER(PARTITION BY user_id, category) / COUNT(sales_id) OVER(PARTITION BY user_id) AS category_rate FROM sales INNER JOIN users USING(user_id) INNER JOIN items USING(item_id) ) -- 性別ごとに平均 , category_rate_avg as ( SELECT DISTINCT sex , category , SUM(category_rate) OVER(PARTITION BY sex, category) / SUM(category_rate) OVER(PARTITION BY sex) as category_rate FROM category_rate_per_user ORDER BY sex , category ) SELECT * FROM category_rate_avg ;
このようにウィンドウ関数をグルーピングに使うと集計の幅が広がるので、ぜひ実践してみていただきたい。
BigQueryの分割テーブルをちょっとだけ完全に理解する
新しい会社に来て初めてGoogle BigQueryに触っているので、新しく学んだ概念や機能を備忘録として記していきたい。
今日のテーマは分割テーブル。
分割テーブをざっくり要約すると……
- 分割テーブルは、巨大なテーブルに対するクエリのパフォーマンスを上げる手段
- 分割テーブルは、見た目上は一つのテーブルだが、実際にはレコードの日付(時間)によって複数の場所に分散して格納されている
- 分割テーブルを作るには、通常のテーブル作成手順+αでOK
- 分割テーブルにクエリを投げる際、日付(時間)で絞り込みをかけることでクエリのパフォーマンスが向上する
分割テーブルの概要
分割テーブルを使う目的
BigQueryにクエリを投げる際、意図した集計結果が素早く返ってくることは非常に重要だ。
いくらビッグデータに特化したBigQueryといえども、レコードが数億、数十億と蓄積されていくと、やはりパフォーマンスは悪化していく。
特にアプリの行動ログやサイトの閲覧ログは途轍もないデータ量のことが多いので、愚直に集計するとエディタの前で結果を待つ時間が長くなり、分析にスピード感が出ない。
クエリが遅くなる原因の一つは、テーブルが巨大すぎてスキャンに時間がかかるところにある。
実際に必要なデータが1ヶ月分だとしても、期間の始まりと終わりを特定するためには余計なレコードまで舐める必要があり、スキャンにかかる時間はテーブルのレコード量に依存してしまう。
分割テーブルの仕組み
上記の問題を解決(または緩和)する手段の一つが、分割テーブルだ。
分割テーブルは、見かけ上は一つのテーブルとして扱うことができる。
しかし実際には、レコードは日付ごとに別々の場所(これをパーティションという)に格納されている。
こうすることで、WHERE句で日付で絞り込んだ際に、スキャン範囲を特定のパーティションに限定することが可能になり、結果的にクエリが速くなる。
分析では普通「昨日のアプリ起動数」や「先月のコンバージョン数」のように集計期間を限定するので、特定期間のパーティションだけをスキャンするこの仕様は、短気な分析者にとっては非常に恩恵が大きい。
分割テーブルの作り方
では早速、分割テーブルを作ってみる。
今回は例として下記のような簡単な構造を持った購買ログを分割テーブルとして作ってみたい。
salesテーブル
カラム名 | 説明 | |
---|---|---|
id | STRING | 売上一つごとに一意なID |
user_id | STRING | 購買者のID |
item_id | STRING | 商品のID |
sales_time | TIMESTAMP | 購入時刻 |
分割テーブルを作る際に決めなければならないのは、「何を基準にパーティションを分割するか」だ。
具体的には
- レコード挿入日で分割する
- TIMESTAMP型またはDATE型のカラムで分割する
という2つの方法から選ぶことになる。
売上がリアルタイムでテーブルに書き込まれる前提ならレコード挿入日で分割すれば良いし、一度バッチを噛ませてから翌日に一気にレコードが挿入される等、ビジネス上意味のある時間とレコード挿入時間にギャップが生じる場合はカラムで分割する形の方が良いだろう。
大事なのは「集計クエリのWHERE句で範囲を絞り込む時に使用する日付を指定する」こと。
Web UIでテーブルを作る際は、次図のように「No Partition」と書かれたドロップダウンリストをクリック。
その上で、レコード挿入日で分割する際は「Partition by ingestion time」を、カラムで分割する場合は「Partition by field」以下のカラム名をクリックすればよい。
また分割テーブルを作成する際にオプションで、クエリでWHERE句で日付の絞り込みを行うことを強制することができる。
せっかく分割テーブルを作っても日付の絞り込みをしなければ結局レコードを片っ端から読みに行ってしまうので、分割テーブルの利点を活かすためにもチェックをつけておくと良いだろう
(余談だが、Treasure Dataではtime列での絞り込みをかけないと警告が出る仕様になっていた。)
方法は下記の通り、「Requiring partition filter」チェックをつけるだけ。
分割テーブルへのクエリの書き方
分割テーブルにクエリを投げる際に気をつけるべき点は1点だけで、日付による絞り込みを必ず行うことである。
テーブル作成時にレコード挿入日での分割を選択した場合は、_PARTITIONDATE
という隠れカラムが生成されているので、このカラムで絞り込みを行う。
select id , user_id , item_id , sales_time from sales where _PARTITIONDATE between '2018-01-01' and '2018-01-31' ;
テーブル作成時にカラムでの分割を選択した場合は、単にそのカラムをWHERE句に書けばよい。
select id , user_id , item_id , sales_time from sales where sales_time >= '2018-01-01' and sales_time < '2018-02-01' ;
なお、絞り込みに使うカラム(上記でいう_PARITIONDATE
やsales_time
)をWHERE句に書く場合、比較演算子の左側にはそのカラムを単独で書かなければ分割テーブルの恩恵が得られないので注意。
ここらへんの詳しい仕様は公式ドキュメントを参照のこと。
物体検知系のネットワークの解説リンクまとめ
物体検知問題を解くための様々なネットワークについて調べていたので、備忘録も兼ねて参考になったリンクをまとめておく。
全般
下記の記事に目を通しておけば、たいていのネットワークの知識は網羅できる。
Deep Learningによる一般物体検出アルゴリズムの紹介
各ネットワークの参考リンク
R-CNN
- 2012年のILSVRCで圧勝したモデル
- 物体の候補の選定、CNNによる画像特徴量の抽出、SVMによる分類という3つのモデルを複合している
- 選定した候補ごとにCNNにかけるので、非常に重い
- モデルそれぞれ別々に学習しなければならず、チューニングしづらい
SPPNet
- 選定した候補ごとにCNNにかける代わりに、1回の畳込みで可変の領域の特徴量を抽出する
- 特徴量抽出まではR-CNNに比べて非常に速くなったが、相変わらず一つのネットワークではないためチューニングしづらい
Fast R-CNN
- RoI Poolingという、多様なサイズの畳込みを行うプーリング操作を行い、領域候補の特徴量抽出を高速化
- バウンディングボックスと分類のそれぞれを同時に学習することで、領域候補提示より後の一つのネットワークでの学習を可能とした
論文紹介: Fast R-CNN&Faster R-CNN
what is ROI layer in fast rcnn?
Faster R-CNN
- 領域候補の提案をRPN(Region Proposal Network)というネットワークで実現し、全ての層を一つのネットワークにすることができた
私がわかりにくいと思った「Faster RCNN」のポイントの解説
論文紹介: Fast R-CNN&Faster R-CNN
YOLO
- 画像をセルに分割し、セルごとにバウンディングボックスの提案を行う
- 各セルに対し、各クラスである条件付き確率を割り当てる
オブジェクト検出 YOLO YOLO — You only look once, real time object detection explained
SSD
- サイズの異なるfeature mapを多数抽出し、それぞれにバウンディングボックスを提案させる
リアルタイム物体検出向けニューラルネット、SSD(Single Shot Multi Detector)及びその派生モデルの解説
RetinaNet
- Negativeだとすぐ分かるようなバウンディングボックスに小さな重みをつけるような損失関数を使うことで、価値ある教師データでのみ学習できるようになり、学習効率が向上
CNN系ネットワーク(AlexNet,VGG,ResNet等)の解説リンクまとめ
画像分類問題を解くためのCNN系の様々なネットワークについて調べていたので、備忘録も兼ねて参考になったリンクをまとめておく。
全般
下記2つの記事に目を通しておけば、たいていのネットワークの知識は網羅できる。
特に前者は記述量に圧倒される。一読の価値あり。
後者はあっさりめなので、さっと軽い気持ちで読める。
畳み込みニューラルネットワークの最新研究動向 (〜2017)
Neural Network Architectures
各ネットワークの参考リンク
LeNet5
- 1944年にYann LeCunが発表した最初のCNN
- 畳み込みのフィルタをパラメータとして学習させるというCNNのアイディアはここで生まれた
定番のConvolutional Neural Networkをゼロから理解する
Convolutional Neural Networkとは何なのか
AlexNet
- 2012年にAlex Krizhevskyが発表したLeNetを大幅に改良したネットワーク
- ImageNetの画像分類コンテストで2位以下に大差をつけて圧勝し、深層学習が画像認識に有効であることを初めて広く世に知らしめた
- 活性化関数にReLU関数を利用したり、Dropoutにより過学習の抑制したりといった、後世でも利用される手法を生み出した
VGG
- オックスフォード大学が発表したモデル
- 9x9や11x11のサイズのフィルターを使用する代わりに、3x3のフィルターを何層にも積み重ねて使用
http://cedro3.com/ai/mini-vgg-net/
Network-in-network
- 線形な畳み込み層の代わりに小さなニューラルネットワーク(MLPConvと呼ぶ)を挿入することで、非線形な特徴を抽出しやすくした
- 出力層にて、過学習しやすい全結合層の代わりに、クラス数と同じチャンネル数の層を置き、各チャンネルの平均をもって各クラスのスコアとする形とした
Network In Network architecture: The beginning of Inception
Network In Networkを動かしてみた
GoogLeNet
- 色々なサイズのフィルタをパラレルに適用し、大きなフィルタと同等の精度を得るInceptionという考え方を踏襲
- 各フィルタに入力を手渡す前に1x1畳み込みで次元削減している
ResNet
- Microsoftが2015年に発表
- 層を深くしすぎると起きる勾配消失問題を、Shortut Connection(重み層の後の活性化関数に、重み層を経る前の値も一緒に入力)という考え方で解決した
[Residual Network(ResNet)の理解とチューニングのベストプラクティス]
(https://deepage.net/deep_learning/2016/11/30/resnet.html)
Squeeze Net
- 同じ精度のAlexNetに比べて3倍高速で500倍省メモリ
- Fire Moduleという、1x1の畳み込み→活性化関数→1x1の畳み込みor3x3の畳み込み→活性化関数という一連の流れを、何層にも積み重ねたもの
「はじめよう位相空間」感想 ー 挫折しない位相空間の学び方
情報幾何を勉強するための前準備として位相空間を学び直していたところ、「はじめよう位相空間」という痒いところに手が届く教科書に巡り会えたので、紹介する。
学生の頃は位相空間に手も足も出なかった私でも大まかなイメージを掴むことができたので、位相空間をこれから勉強する人、一度挫折した人には非常にオススメ。
- 作者: 大田春外
- 出版社/メーカー: 日本評論社
- 発売日: 2000/12/01
- メディア: 単行本
- 購入: 6人 クリック: 39回
- この商品を含むブログ (14件) を見る
位相空間は何故学びづらいのか
位相空間論は、専門的な数学を勉強する上で必須の知識にもかかわらず、抽象的で挫折率が高い科目として悪名高い。
学生の頃の私も、位相空間の定義を見るたびに教科書をそっ閉じしていたクチで、殆ど何も理解できなかった。
位相空間論の困難さの原因は色々とあるのだが、私は次の2つが特に大きいと思う。
1. 連続写像、同相写像のイメージが湧きづらい
位相空間というのは、かいつまんで言うと連続写像や同相写像の定義できる空間のことだ。最初に連続写像、同相写像のイメージを持っておくことは、後の学習で大いに助けになる
にもかかわらず、連続写像や同相写像の厳密な定義は、予備知識なしにそれだけ読んでも到底イメージが湧きづらい
連続や同相のイメージなしに記号の渦の中を突き進んでも、いったいそれが何を表象しているのか見当がつかないため、途中で挫折する羽目になる。
2. 距離空間と位相空間のギャップが甚だしい
距離空間の定義は、「距離」という日常に根ざした概念に依拠しているため、ある程度直感的に把握しやすい。
しかし位相空間の定義となると、「開集合族」というなんだかよく得体の知れないフワフワしたもので記述されており、お化けを相手にしているかのような不気味さがある。
この「距離」と「開集合族」という2つの概念の乖離のために、位相空間がこの世のものではないかのように感じてしまって学習に身が入らない。
「はじめよう位相空間」は上記の問題にいかに応えているか
「はじめよう位相空間」は、上記の2つの問題に見事に応えている。
1. 連続写像、同相写像のイメージが明瞭
この本では全編を通じて写像を図形の変換に対応させて説明しており、連続写像と同相写像は次のように紹介される
この図形的なイメージのおかげで、読者は「連続or同相とは何か」という抽象的な問いを「図形を破るor貼り付けるとは何か」という具体的な問に置き換えて読むことができるようになっており、迷子にならずに済む
2. 距離空間から位相空間へのシームレスな接続
連続・同相の定義は、距離空間では比較的直観的に定義することができる。
これは、距離という概念が我々にとって馴染みがありイメージがつきやすいからだと思う。
しかし実は、連続・同相の概念は、直接的に距離を使わなくても記述することができる(具体的には「同相写像とは、開集合を開集合に移す写像」という形で書ける)
この「距離から開集合へ」というパラダイムシフトこそ、位相空間を理解する上での最大のキモかつ難所だと思うのだが、この本ではそのギャップを埋めるのに2章を費やしており、説明も丁寧だ。
位相空間を開集合族の振る舞いによって定義する方法が、非常に自然なものであることが理解できるよう、最大限の配慮がなされていると思う。
読む上での留意点
予備知識
この本を読む上で必要な知識は殆どないが、ε-δ論法による連続関数の定義を復習しておくとスムーズに読める。
読み進め方
時間があれば1ページずつ丹念に読めばよいと思うが、私は手っ取り早く大要をつかみたかったので、問題や定理の証明は全てすっ飛ばして1日で読んだ
そういう雑な読み方をしても、本文が充実しているために全体のストーリーは十分に理解できた。
2週目は、きちんと落ち着いて1行ずつ咀嚼していきたい。
発展的な学習
本文にも書いてあるが、この本は位相空間の詳細な性質を追うよりは直観的なイメージを掴むところに重きを置いているため、この本をマスターしたら別の本格的な教科書(松坂本とか内田本とか)を読むことが必要と思われる。
感想
苦手意識の強かった集合位相論に対し、何とか取っ組める自信がついてきたので、いずれは多様体や微分幾何、最終的には情報幾何を勉強していきたい。
- 作者: 大田春外
- 出版社/メーカー: 日本評論社
- 発売日: 2000/12/01
- メディア: 単行本
- 購入: 6人 クリック: 39回
- この商品を含むブログ (14件) を見る
TensorFlowでRNN(LSTM)実装
初めてRNN(LSTM)を実装したので備忘録として。
目標
- TensorFlowではじめるDeepLearning実装入門の第4章に従い、TensorFlowでMNISTを分類するRNN(LSTM)を実装してみる。
- ついでに、TensorBoardに正解率等を表示する。
※ LSTMの理論的説明はこちらを御覧ください。
方針
- MNISTの各画像を、上から1行ずつスキャンし、時系列データとしてLSTMに入力
- LSTMの最後の中間層の次の全結合層を出力層とする
コード
from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf #mnistデータを格納したオブジェクトを呼び出す mnist = input_data.read_data_sets("../data/mnist", one_hot=True) def seq_mnist(): # ログのリスト summaries = [] # データのサイズ num_image = 784 # 画像のピクセル数 num_class = 10 # 正解のクラス数 num_input = 28 # RNNの入力長 num_seq = 28 # RNNの時間長 # データの定義 with tf.name_scope("data"): # 入力画像 x = tf.placeholder(tf.float32, [None, num_image]) # 正解ラベル y = tf.placeholder(tf.float32, [None, num_class]) # LSTM with tf.name_scope("lstm"): # 画像を1行ずつ読み込みよう整形 input = tf.reshape(x, [-1, num_seq, num_input]) # ユニット数128個のLSTMセル stacked_cells = [tf.nn.rnn_cell.LSTMCell(num_units=128) for _ in range(2)] cell = tf.nn.rnn_cell.MultiRNNCell(cells=stacked_cells) out_lstm, states = tf.nn.dynamic_rnn(cell=cell, inputs=input, dtype=tf.float32) # 最後の層を取得 out_lstm_list = tf.unstack(out_lstm, axis=1) lstm_last = out_lstm_list[-1] # 出力層 with tf.name_scope("out"): # 出力層定義 w = tf.Variable(tf.truncated_normal([128, 10], stddev=0.1)) b = tf.Variable(tf.zeros([10])) out = tf.nn.softmax(tf.matmul(lstm_last, w) + b) # 訓練 with tf.name_scope("train"): # 誤差 loss = tf.reduce_mean(-tf.reduce_sum(y * tf.log(out), axis=[1])) # ログ出力 summaries.append(tf.summary.scalar("loss", loss)) # 訓練 train_step = tf.train.GradientDescentOptimizer(0.1).minimize(loss) # 評価 with tf.name_scope("evaluation"): # 正解率 correct = tf.equal(tf.argmax(out, 1), tf.argmax(y, 1)) accuracy = tf.reduce_mean(tf.cast(correct, tf.float32)) # ログ出力 summaries.append(tf.summary.scalar("accuracy", accuracy)) # 初期化 init = tf.global_variables_initializer() with tf.Session() as sess: # 初期化 sess.run(init) # ログをひとまとめにする設定 summary_op = tf.summary.merge(summaries) # ログの保管場所 summary_writer = tf.summary.FileWriter("../logs", sess.graph) # テストデータ test_images, test_labels = mnist.test.images, mnist.test.labels # 学習 for i in range(1000): step = i + 1 train_images, train_labels = mnist.train.next_batch(50) sess.run(train_step, feed_dict={x: train_images, y: train_labels}) # 定期的に正解率確認 if step % 100 == 0: # ログのテキスト取得 summary_text, acc_val = sess.run([summary_op, accuracy], feed_dict={x: test_images, y: test_labels}) # ログに書き出し summary_writer.add_summary(summary_text, step) # 正解率表示 acc_val = sess.run(accuracy, feed_dict={x: train_images, y: train_labels}) print("step: {} acc: {:.3}".format(step, acc_val)) seq_mnist()
TensorBoardの出力
Accuracy
Loss
グラフ
TensorFlowの関数を使うと非常に短いコードで実装できるが、中で何が起きているかはよく分からない。