TensorFlowではじめる物件探し〜その1

2015年11月にGoogleから公開されたTensorFlow。
公式チュートリアルではMNISTの手書き数字認識がテーマになっています。
https://www.tensorflow.org/get_started/mnist/beginners

なっていますが、なにかもっと自分の生活にも役に立ちそうなことをしたいと思って、「物件探し」をテーマにしてみました。

# おことわり
- シリーズものとして書きたい（予定）。
- 試してから記事公開までに時間が空いてしまっています。


# MNIST手書き数字認識

[MNIST手書き数字認識](http://yann.lecun.com/exdb/mnist/)は、人工知能、特に画像認識の界隈では言わずと知れた問題です。0から9までの手書き文字を入力とし、それが0から9のどの数字であるか判定するタスクです。
テストデータの中で正しく判定できる割合が高いアルゴリズムほど、（単純に言えば）「良い」ことになります。

データ数、入力データ・出力データの次元はこのようになっています。

```
num_of_data=70,000(60,000 training data, 10,000 test data)
input=784 (28*28ピクセル)
output=10 (0から9までのラベル)
```

※TensorFlowチュートリアルの[スクリプト](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/mnist/input_data.py)では、training dataのうち5000件をvalidationデータに充てているようです。


画像とラベルのtraining, validation, test データの次元

```
---- _images ----
(55000, 784)
(5000, 784)
(10000, 784)
<type 'numpy.ndarray'>
---- _labels ----
(55000, 10)
(5000, 10)
(10000, 10)
(agile_env)tensorflow $ pwd
```

# 物件探し：問題設定

「MNISTの手書き数字認識」を参考に、「物件探し」に関してどういう問題を設定するか考えてみます。今回は物件の中でも特に賃貸物件を想定することにします。

賃貸物件を探すとき、住みたいエリアなどの条件から物件を絞るのが一般的ですが、「最寄駅や間取りなど自分が抱いている希望に対して、月々の家賃をどのくらい支払うのが妥当か」という点は、どこかの段階で必ず気になる観点だと思います。

他の物件データを活用して「妥当な家賃の目安」のようなものを導き出せれば、自分が興味のある物件の家賃が、割安なのか割高なのか分かる、かもしれない・・・？と思いました。

一件一件特徴が異なる不動産は、価格予測が成り立ちにくい世界ではありそうですが、少なくとも自分にとっては手書き数字の認識よりは興味の持てそうな問題だったので、やってみます。

```
◯◯駅まで徒歩10分、1LDKで築10年のところに住みたい。
・月◯◯万円って言われたんだけど、お買い得なんだろうか？
・目安としては××万円らしい。◯◯万円って高すぎない？
```

MNISTのデータに準じる形で、入力データと出力データを考えてみましょう。
[ニューラルネットワークでプロ野球選手の給与を査定してみる](http://qiita.com/sergeant-wizard/items/9bb45c0850aebca2bc07) を参考にしました。

*データ形式、データソースなど、これ以降の設定は、分析が進むにつれて頻繁に変更していく予定です。

```
input=34(29+1+3+1)下記参照
output=10

*inputの種類
・最寄駅：山手線の29駅（one-hot vector） *多すぎたら減らす、少なすぎたら増やす（他の路線？）。
	品川駅を起点に外回り。品川駅:inputの第0要素,大崎駅:1,...
・最寄駅まで徒歩◯分（データ中の最大値をもとに0-1に正規化）
・間取り：1LDK、2DK、2LDKの3種類 (one-hot vector) *著者が住みそうな間取り
・築年数 (データ中最大値で0-1に正規化)
##・敷金の有無
##・礼金の有無
##・面積 (データ中最大値で正規化)
##・マンション、アパート: (one-hot vector)
…

*outputの種類
0: 7.5万円未満
1: 7.5-8.0万円未満
2: 8.0-8.5万円未満
3: 8.5-9.0万円未満
4: 9.0-9.5万円未満
5: 9.5-10.0万円未満
6: 10.0-10.5万円未満
7: 10.5-11.0万円未満
8: 11.0-11.5万円未満
9: 11.5万円以上
```


# データの収集

今回は、某大手不動産・住宅に関する総合情報サイトの検索結果を利用してみます。例えば、「JR山手線、1LDK／2DK／2LDK、ページあたり表示100件」みたいな条件を指定。

pythonなどでクロールして、例えばこういう形で収集します。

```
train_ＪＲ山手線/田端駅 14 2DK 15 11.8000001907 [物件のURL]
train_ＪＲ山手線/田端駅 14 2DK 15 11.8000001907 [物件のURL]
train_ＪＲ山手線/田端駅 14 2DK 15 11.8000001907 [物件のURL]
train_ＪＲ山手線/田端駅 14 2DK 15 11.8000001907 [物件のURL]
…
```

全部で、29786件（実行当時）。


# データの整形・読み込み

TensorFlowのチュートリアルで提供されているinput_data.pyを修正して、
csvとして用意したinputデータをそのまま読み込めるようにします。

training, validation, testデータの比率を決めます（22000, 2000, 5786）。

```
split -l 24000 attributes.csv
(after splitting, rename to train- and test- attributes.csv)
split -l 24000 labels.csv
(after splitting, rename to train- and test- labels.csv)

REAL_ESTATE_data $ tail -5 train-attributes.csv 
0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.06451612903225806,1,0,0,0.36666666666666664
0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.01935483870967742,0,1,0,0.6333333333333333
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0.0967741935483871,0,0,1,0.1111111111111111
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1032258064516129,1,0,0,0.32222222222222224
0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05161290322580645,1,0,0,0.1
REAL_ESTATE_data $ tail -5 train-labels.csv 
8
9
9
6
9
REAL_ESTATE_data $ tail -5 test-attributes.csv 
0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0967741935483871,0,0,1,0.25555555555555554
0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05806451612903226,0,1,0,0.34444444444444444
0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.12258064516129032,0,1,0,0.35555555555555557
0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.03870967741935484,0,1,0,0.5
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.16129032258064516,1,0,0,0.022222222222222223
REAL_ESTATE_data $ tail -5 test-labels.csv 
9
9
9
9
9
REAL_ESTATE_data $ 
```

extract_images(filename)はcsvをそのまま読み込むだけ（もはや”images”ではないですが）。

```
def extract_images(filename):
  print('Extracting images ', filename)
  data = numpy.genfromtxt(filename, delimiter=',') # default is dtype=float
  return data
```

DataSetクラスも、次元の変換が不要なので省きます。

```
class DataSet(object):
  
  def __init__(self, images, labels, fake_data=False):
    if fake_data:
      self._num_examples = 10000
    else:
      assert images.shape[0] == labels.shape[0], ( "images.shape: %s labels.shape: %s" % (images.shape, labels.shape))
      self._num_examples = images.shape[0]

    self._images = images
    self._labels = labels
    self._epochs_completed = 0
    self._index_in_epoch = 0
…
```


# モデル

TensorFlowのチュートリアル（初心者向け）に沿って、Softmax Regression モデルを構築します。
Softmax Regression モデルは一般的に、あるものが、いくつかの異なる候補のうちいずれかになる確率を割り当てたい時によく使用されるモデルのようです。
手書き数字認識の例で言えば、ある画像に対して、「9」である確率を80%、「8」である確率を4%・・・と割り当てていくイメージです。

```
[0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.04, 0.80]
```


```
softmax: exponentiating its inputs and then normalizing them. 

exponentiation:
- one more unit of evidence increases the weight given to any hypothesis multiplicatively. 
- one less unit of evidence means that a hypothesis gets a fraction of its earlier weight. 
- No hypothesis ever has zero or negative weight. 

normalization:
- they add up to one, forming a valid probability distribution.
```


# はじめての結果

```
(agile_env)tensorflow $ python intro_mnist_refactor.py
##### prepare and read data set #####
Extracting images  REAL_ESTATE_data/train-attributes.csv
Extracting labels  REAL_ESTATE_data/train-labels.csv
Extracting images  REAL_ESTATE_data/test-attributes.csv
Extracting labels  REAL_ESTATE_data/test-labels.csv
##### init and run session #####
can't determine number of CPU cores: assuming 4
I tensorflow/core/common_runtime/local_device.cc:25] Local device intra op parallelism threads: 4
can't determine number of CPU cores: assuming 4
I tensorflow/core/common_runtime/local_session.cc:45] Local session inter op parallelism threads: 4
##### training #####
##### evaluation #####
0.859834
(agile_env)tensorflow $ 
```

テストデータが5786件だったので、そのうち5000件弱は正しく家賃を判定できたようです。
この数字だけ見ると、そこそこよい判定ができたのか・・・？


# データを見つめると

家賃のラベル付けの段階から気がついていた方もいるかもしれませんが、
9（＝今回は家賃が11.5万円以上）が圧倒的に多い・・・！
データセットを読み込んだ後で、こんな形で集計すると・・・。

```
print mnist.train._labels.shape
print mnist.validation._labels.shape
print mnist.test._labels.shape

print numpy.sum(mnist.train._labels, axis = 0)
print numpy.sum(mnist.validation._labels, axis = 0)
print numpy.sum(mnist.test._labels, axis = 0)
```

```
(22000, 10): training
(2000, 10): validation
(5786, 10): test
[   127.    158.    199.    235.    314.    407.    442.    539.    598.  18981.]
[    7.    10.    25.    19.    33.    33.    38.    49.    47.  1739.]
[   48.    41.    51.    71.    84.   123.   113.   133.   141.  4981.]
```

9にラベル付けされるデータが、他と比べて非常に多いです。
標準偏差は、

```
numpy.std(numpy.sum(mnist.train._labels, axis = 0))
numpy.std(numpy.sum(mnist.validation._labels, axis = 0))
numpy.std(numpy.sum(mnist.test._labels, axis = 0))
5595.73064041
513.175213743
1467.88052647
```


MNISTのデータでは、どうでしょうか。

```
(55000, 10): training
(5000, 10): validation
(10000, 10): test
[ 5444.  6179.  5470.  5638.  5307.  4987.  5417.  5715.  5389.  5454.]
[ 479.  563.  488.  493.  535.  434.  501.  550.  462.  495.]
[  980.  1135.  1032.  1010.   982.   892.   958.  1028.   974.  1009.]
```

手書き文字で0から9のデータを、ある程度バランスよく集めていると言えそうです。標準偏差は、

```
291.905806725
37.6218021897
59.1962836671
```


# 次回は

0から9までのデータをバランスよく集めたり、データの中身を見たりしながら、また分析を試してみる予定です。