盆暗の学習記録

データサイエンス ,エンジニアリング,ビジネスについて日々学んだことの備忘録としていく予定です。初心者であり独学なので内容には誤りが含まれる可能性が大いにあります。

[Windows]エクスプローラーのアドレスバーが思ったより便利だった

Windowsの小ネタです。

エクスプローラーのアドレスバーってパスを指定するくらいしか出来ないのかと思っていたのですが,プログラムを実行したりできるみたいです。

f:id:nigimitama:20181224023304p:plain

1. プログラムの実行

例えばpythonと実行すると,

f:id:nigimitama:20181224023454p:plain

f:id:nigimitama:20181224023533p:plain

pathが繋がっているAnaconda上のPythonが立ち上がります。

同様に,例えばメモ帳(notepad.exe)を開きたいときはnotepadで開きますし,RStduio(rstudio.exe)もrstudioで立ち上がります。

ちょうど「ファイル名を指定して実行」(Win + Rで起動するアレ)と同じような働きをしてくれます。

f:id:nigimitama:20181224024220p:plain

2. 任意のディレクトリでコマンドプロンプトを起動

あるディレクトリをエクスプローラーで開いている状態で,アドレスバーからコマンドプロンプトを起動(cmd)すると,開いているディレクトリをカレントディレクトリにした状態のコマンドプロンプトが開きます。

例えばマイピクチャで実行すると,

f:id:nigimitama:20181224023654p:plain

f:id:nigimitama:20181224023706p:plain

f:id:nigimitama:20181224023723p:plain

このように。

WSLのUbuntuではできない

残念ながら,Windows Subsystem for LinuxUbuntuは同様の動きをしませんでした

f:id:nigimitama:20181224024335p:plain

f:id:nigimitama:20181224030334p:plain

Bash on Ubuntu on WindowsWindows Subsystem for Linuxに改名する前,試験的に実装されていた時代のもの)のbash.exeはcmdのように動いてくれるんですが…

なぜWSLにしたときにこの部分を改悪したんだ…(追記参照)

f:id:nigimitama:20181224030045p:plain

f:id:nigimitama:20181224031847p:plain

追記:WSLはwsl.exeかbash.exeで実行すればいい

Bash on Ubuntu on WindowsからWSLに移行した後に購入したPCでもbash.exeで実行できました。WSLのインストール時にbash.exeもインストールされるのかな?

bash.exeだけでなくwsl.exeというものもあり,こちらでも同様の機能を果たしてくれます(bashとwslの違いがよくわかりません…)

ubuntu.exeを実行したとき開かれるのもwsl.exeのようなので,ubuntuは外側で中身はwslなのかも・・?

f:id:nigimitama:20181227152359p:plain

いずれにしても,任意のディレクトリでターミナルを開きたいときにはwsl.exebash.exeを使えば良さそうです。

TensorFlow-GPUのインストールで詰まったときのメモ

久々にTensorFlowを使おうとしたら動かなくて(最近Rでkerasをインストールするときに失敗したから…?),再インストールしようとしたら詰まったので対処法をメモ

対処法というのもおこがましいレベルで,要するに「最新版じゃなく,少し古いバージョンのTensorFlowを入れたら(よくわからんが)動くようになった」という感じです…(汗)

※環境

Windows 10(ver.1803)
NVIDIA GeForce GTX 760
Python 3.6
Anaconda使ってます

今回の問題と対応

問題:GPUが認識されない

私は当初tensorflow-gpu 1.12.0(最新)とcuDNN 7.4(最新)とCUDA9.0で行っていたのですがGPUが認識されませんでした。

対応

いろいろといじっていたのですが,試しにtensorflow-gpuを1.10.0にダウングレードしてみたところ,GPUが認識されるようになりました。

※ダウングレードの方法

pip uninstall -y tensorflow-gpu
pip install tensorflow-gpu==1.10.0

以下備忘録

TensorFlowの環境構築

TensorFlowのインストール方法

公式(https://www.tensorflow.org/install/)にいけば書いてあるんですが,あえて日本語で整理しておきます(自分用に…)

tensorflow

CPU onlyのTensorFlowをインストールする場合は単純で,

  1. Anacondaのインストール(これでpythonとpipが入る)
  2. Anaconda promptでpip install tensorflow を実行する

です

tensorflow-gpu

しかし,TensorFlow-GPUのインストールの場合はもうすこし面倒で,

  1. Anacondaのインストール(これでpythonとpipが入る)
    • 今回はAnacondaで新しい仮想環境を"Create"した
  2. CUDA対応のグラボとドライバを用意する
  3. CUDAをインストールする
  4. cuDNNをインストールする
    • Windowsの場合)ダウンロードしたzipファイルの中身をCUDAのディレクトリに手動コピーします
  5. Anaconda promptでpip install tensorflow-gpuを実行する

という手順になります。

気をつける点

tensorflow-gpuとcuDNNとCUDAのバージョンを対応させる必要があって,公式ページに対応表があります。

https://www.tensorflow.org/install/source_windows#gpu

ただ,今回はこの対応表通りにやっても動かないパターンでした…

GPUが認識されているかを確かめる方法

pythonエディタで実行できるなら以下のコードで。

# tensorflowがGPUも使えるかどうか
from tensorflow.python.client import device_lib
print(device_lib.list_local_devices())

Anaconda promptから確認したいなら

python -c "from tensorflow.python.client import device_lib; print(device_lib.list_local_devices())"

で。

GPUが認識されていない場合,device_type: "CPU"だけしか返ってきません。

[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: 1714209209365535369
]

GPUが認識されていればdevice_type: "GPU"の文字が返ってきます

[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: 16550052200207927911
, name: "/device:GPU:0"
device_type: "GPU"
memory_limit: 1481696870
locality {
  bus_id: 1
  links {
  }
}
incarnation: 10180044052055237741
physical_device_desc: "device: 0, name: GeForce GTX 760, pci bus id: 0000:01:00.0, compute capability: 3.0"
]

[R]Webサイトをスクレイピングしてxlsxをダウンロードする

  1. Rでスクレイピングするときの基本的な手順
  2. Rでネット上のデータをダウンロードするときの方法

についてメモ。

やりたいこと

例えば,以下のサイトで

http://www.soumu.go.jp/senkyo/senkyo_s/data/shugiin46/shikuchouson.html

f:id:nigimitama:20181218160957p:plain

  1. 「北海道」から「沖縄県」の各リンクをたどって,
  2. 各リンク先のすべてのxlsファイルのリンクを取得し,
  3. すべてのxlsファイルをダウンロードしたい

という状況を考えます。

①{rvest}によるスクレイピング

1.「北海道」から「沖縄県」の各リンクをたどって,
2.各リンク先のすべてのxlsファイルのリンクを取得

するところまでをスクレイピングで行います

# ライブラリ読み込み
library(tidyverse)
library(readr)
library(rvest)

1. htmlの読み込み

read_html()に目的のサイトのURLを入れ,htmlを取得します

# read_html():htmlの読み込み
html_data = read_html("http://www.soumu.go.jp/senkyo/senkyo_s/data/shugiin46/shikuchouson.html")

# 中身の表示
html_data
> # 中身の表示
> html_data
{xml_document}
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
[1] <head>\n<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n<meta http-equiv="Content-Style-Type" content="text/ ...
[2] <body>\r\n<a name="pTop" id="pTop"></a>\r\n<noscript><div id="nsBelt"><div class="nsMsg">すべての機能をご利用いただくにはJavascriptを有効にして下さい。</di ...

2. 目的のタグ・URLを抽出

html_nodes()cssxpathを指定して目的のタグを抽出できます

div要素を抽出

今回の目的のサイトの,各都道府県のページへのリンクがある領域はbodyAreaというidのdivタグの中にあるので,まずbodyAreaの中の情報だけを取り出してみます。

Google Chromeの「検証(Ctrl+Shift+I)」から目的のタグで右クリックしてメニューを開き,「Copy XPath」をクリックすればxpathが入手できるので,Rに貼り付けます

f:id:nigimitama:20181218162436p:plain

# html_nodes():cssやxpathを指定して目的のタグを抽出
bodyArea = html_data %>% html_nodes(xpath = '//*[@id="bodyArea"]')
bodyArea
> bodyArea
{xml_nodeset (1)}
[1] <div id="bodyArea">\r\n<h4>市区町村別得票数</h4>\r\n\r\n\r\n<ul class="inline arrowlink">\n<li><a href="/senkyo/senkyo_s/data/shugiin46/s ...

a要素を抽出

次に,「北海道」から「沖縄県」までの各ページのURLには"shikuchouson"という言葉が入っているようなので,cssセレクタで「リンクタグ(<a></a>)でhrefに"shikuchouson"を含むもの」という意味のa[href *= 'shikuchouson']という条件を指定して抽出します。

f:id:nigimitama:20181218162050p:plain

a_tags = bodyArea %>% html_nodes(css = "a[href *= 'shikuchouson']")
a_tags
> a_tags
{xml_nodeset (47)}
 [1] <a href="/senkyo/senkyo_s/data/shugiin46/shikuchouson_01.html">北海道</a>
 [2] <a href="/senkyo/senkyo_s/data/shugiin46/shikuchouson_02.html">青森県</a>
(中略)
...

URLを抽出

html_attr()を使ってaタグのhref属性に指定されている文字列を取得し,各ページへのURLを取得します

# html_attr():タグの属性に指定されている文字列を取得
urls = a_tags %>% html_attr("href")
urls
> urls
 [1] "/senkyo/senkyo_s/data/shugiin46/shikuchouson_01.html" "/senkyo/senkyo_s/data/shugiin46/shikuchouson_02.html"
 [3] "/senkyo/senkyo_s/data/shugiin46/shikuchouson_03.html" "/senkyo/senkyo_s/data/shugiin46/shikuchouson_04.html"
(以下略)

リンクテキストを抽出

html_text()を使ってタグのテキスト部の文字列を取得します。

texts = a_tags %>% html_text()
texts
> texts
 [1] "北海道"   "青森県"   "岩手県"   "宮城県"   "秋田県"   "山形県"   "福島県"   "茨城県"   "栃木県"   "群馬県"   "埼玉県"   "千葉県"  
[13] "東京都"   "神奈川県" "新潟県"   "富山県"   "石川県"   "福井県"   "山梨県"   "長野県"   "岐阜県"   "静岡県"   "愛知県"   "三重県"
(以下略)  

ループ処理

以上の基礎知識を組み合わせ,47都道府県分をループするように処理を組みます。

前節で47都道府県分の各県のページへのURLとテキストを入手したので,あとはその先のページでhrefに"xls"を含む要素のURLを抽出したりすればいいだけです。

# 47都道府県分ループして各ページのURLを取得する
for(i in 1:length(urls)){
  html_i = read_html(str_c("http://www.soumu.go.jp",urls[i]))
  
  ## i県のxls(小選挙区,比例代表の2つのデータ)のリンク
  urls_i = html_i %>% html_nodes(css = ".linklist") %>% 
    html_nodes(css = "a[href *= 'xls']") %>% html_attr("href")
  
  ## i県のxlsのリンクの文字列(説明文)
  texts_i = html_i %>% html_nodes(css = ".linklist") %>% 
    html_nodes(css = "a[href *= 'xls']") %>% html_text()
  
  ## data frameにする
  url_df_i = data_frame(url = urls_i, text = texts_i, prefName = texts[i])
  if(i == 1) url_df = url_df_i
  if(i != 1) url_df = bind_rows(url_df, url_df_i)
  
  # 1ループごとに0.5秒待ってサーバーへの負荷をへらす
  Sys.sleep(0.5) 
}
url_df %>% head()
> url_df %>% head()
# A tibble: 6 x 3
  url                         text     prefName
  <chr>                       <chr>    <chr>   
1 /main_content/000205177.xls 小選挙区 北海道  
2 /main_content/000205178.xls 比例代表 北海道  
3 /main_content/000205179.xls 小選挙区 青森県  
4 /main_content/000205180.xls 比例代表 青森県  
5 /main_content/000205181.xls 小選挙区 岩手県  
6 /main_content/000205182.xls 比例代表 岩手県  

各xlsファイルのURLと名称に関するテキストが取得できました。

②xlsファイルのダウンロード

3.すべてのxlsファイルをダウンロード

に取り組みます。

download.file(url, destfile)で指定したURLのファイルを,指定したパス(ファイル名)で保存します。

# 取得したURL一覧から順次xlsをダウンロードする
base_url = "http://www.soumu.go.jp/" # URLの前半部分(取得したURLが相対URLなので)
# ダウンロード先フォルダの作成
download_folder = str_c("./xls") # ダウンロード先フォルダ
dir.create(download_folder) # 「xls」というフォルダを作成


# download.file():「指定したURL」から「指定したパス」へのダウンロード
## ファイル名ベクトルの作成:「北海道_小選挙区」型のファイル名
file_name = str_c(url_df[["prefName"]],"_",url_df[["text"]])
## URLベクトルの作成
url = str_c(base_url,"/",url_df[["url"]])
## 各URLをダウンロード
for(i in 1:length(url)){
  download.file(url = url[i],
                destfile = str_c(download_folder,"/",file_name[i],".xls"),
                mode = "wb") # 今回は"wb"がうまくいった
  # 1ループごとに0.5秒待ってサーバーへの負荷をへらす
  Sys.sleep(0.5) 
}

# ダウンロードしたファイルたち
list.files(download_folder)
> list.files(download_folder)
 [1] "愛知県_小選挙区.xls"   "愛知県_比例代表.xls"   "愛媛県_小選挙区.xls"   "愛媛県_比例代表.xls"   "茨城県_小選挙区.xls"  
 [6] "茨城県_比例代表.xls"   "岡山県_小選挙区.xls"   "岡山県_比例代表.xls"   "沖縄県_小選挙区.xls"   "沖縄県_比例代表.xls"
(以下略)

ダウンロードしたxlsファイルは{readxl}で前処理していきます(この部分は長くなるので書きませんが)。

まとめ

上記の処理をまとめると,以下のようになります。

# ライブラリ読み込み
library(tidyverse)
library(readr)
library(rvest)

# 1. スクレイピングしてURLを取得 ===========================
# read_html():htmlの読み込み
html_data = read_html("http://www.soumu.go.jp/senkyo/senkyo_s/data/shugiin46/shikuchouson.html")

# html_nodes():cssやxpathを指定して目的のタグを抽出
bodyArea = html_data %>% html_nodes(xpath = '//*[@id="bodyArea"]')
a_tags = bodyArea %>% html_nodes(css = "a[href *= 'shikuchouson']")

# html_attr():タグの属性に指定されている文字列を取得
urls = a_tags %>% html_attr("href")
# html_text():タグのテキスト部の文字列を取得
texts = a_tags %>% html_text()


# 47都道府県分ループして各ページのURLを取得する ------------
for(i in 1:length(urls)){
  html_i = read_html(str_c("http://www.soumu.go.jp",urls[i]))
  
  ## i県のxls(小選挙区,比例代表の2つのデータ)のリンク
  urls_i = html_i %>% html_nodes(css = ".linklist") %>% 
    html_nodes(css = "a[href *= 'xls']") %>% html_attr("href")
  
  ## i県のxlsのリンクの文字列(説明文)
  texts_i = html_i %>% html_nodes(css = ".linklist") %>% 
    html_nodes(css = "a[href *= 'xls']") %>% html_text()
  
  ## data frameにする
  url_df_i = data_frame(url = urls_i, text = texts_i, prefName = texts[i])
  if(i == 1) url_df = url_df_i
  if(i != 1) url_df = bind_rows(url_df, url_df_i)
  
  # 1ループごとに0.5秒待ってサーバーへの負荷をへらす
  Sys.sleep(0.5) 
}


# 2. ダウンロード ==========================================
# 取得したURL一覧から順次xlsをダウンロードする
base_url = "http://www.soumu.go.jp/" # URLの前半部分(取得したURLが相対URLなので)
# ダウンロード先フォルダの作成
download_folder = str_c("./xls") # ダウンロード先フォルダ
dir.create(download_folder) # 「xls」というフォルダを作成


# download.file():「指定したURL」から「指定したパス」へのダウンロード
## ファイル名ベクトルの作成:「北海道_小選挙区」型のファイル名
file_name = str_c(url_df[["prefName"]],"_",url_df[["text"]])
## URLベクトルの作成
url = str_c(base_url,"/",url_df[["url"]])
## 各URLをダウンロード
for(i in 1:length(url)){
  download.file(url = url[i],
                destfile = str_c(download_folder,"/",file_name[i],".xls"),
                mode = "wb") # 今回は"wb"がうまくいった
  # 1ループごとに0.5秒待ってサーバーへの負荷をへらす
  Sys.sleep(0.5) 
}

# ダウンロードしたファイルたち
list.files(download_folder)

参考文献

Rによるスクレイピング入門

Rによるスクレイピング入門

石田さんのこの本は基礎的なことから幅広く書いてあってスクレイピングするとき手元にあるとすごく良いです。

HackMDというmarkdownエディタがスゴイ

以前こんな記事を書いていました

nigimitama.hatenablog.jp

メモ系のアプリを比較してみて,自分にはTyporaが一番あってるな,と結論づけました。

nigimitama.hatenablog.jp

ところが先日,友人と共同でメモを書きたいな,となったときに,「共同編集が可能なマークダウンエディタ」という観点で調べたところ,HackMDというエディタを見つけました。

このエディタはWebアプリ(=ブラウザ上でPCからもスマホからもアクセスできる)であり,非常に書きやすいのでおすすめしておきます。

hackmd.io

以下ではこのエディタの長所・短所などを簡単に紹介したいと思います。

HackMDの特徴

外見

編集画面は以下の画像の形式(「分割モード」)と,分割モードの左側の画面だけにした「編集モード」があります。 (分割モードの右側の画面だけにした「表示モード」もあり,左上のボタンで簡単に切り替えられます。)

f:id:nigimitama:20181214205227p:plain

良いところ

1. 共同編集・Web公開が可能

  • URLを送るだけで公開・共同編集可能
  • wordみたいに文書の随所にコメントを付与できる

2. webアプリなのでスマホから編集可能

3. 文書はタグで管理する

  • フォルダ管理よりも自由

4. UIが充実している

f:id:nigimitama:20181214210850p:plain

  • 編集画面上部にあるツールバーのアイコンをクリックして操作可能
  • ctrl + ]でインデントを増やす」等のショートカットキーも有効

5. 画像・図表を入れやすい

f:id:nigimitama:20181214220059p:plain

6. 数式を入れられる

7. エクスポートの選択肢が豊富

  • スライド形式でエクスポートすることも可能
  • インポートの選択肢も多い

f:id:nigimitama:20181214221556p:plain

悪いところ

1. フォントの種類や大きさなどを自分で設定できない

  • デフォルトの日本語フォントがあまり綺麗じゃないWindowsにおいては結構気になるところ

今の所思い当たる悪いところがこれくらいです…あれ,これはTyporaを超えるのでは・・・?

おわりに

無料でありながら有料アプリに勝るくらい完成度の高いマークダウンエディタだと思います。

おすすめです。

Rのround()は四捨五入をするわけではない

round関数の意味を誤解したまま使っていて面倒なことになったのでメモ。

round()は四捨五入をするわけではない

偶数への最近接丸め

round()helpを確認してみると,Detailsの部分に

Note that for rounding off a 5, the IEC 60559 standard (see also ‘IEEE 754’) is expected to be used, ‘go to the even digit’.

(5を丸めるときはIEC 60559規格が使われ,”偶数へ丸める”ことになるので注意してください)

と書いてあります。どういうことかというと,偶数への最近接丸め(Round to nearest, ties to even)(偶数丸め)と呼ばれる,丸め誤差を最小化するための方式で,

  1. 端数が0.5より小さいなら切り捨て
  2. 端数が0.5より大きいならは切り上げ
  3. 端数がちょうど0.5なら切り捨てと切り上げのうち結果が偶数となる方へ丸める。

という処理を行うようです(1.3.3偶数への丸め(round to even) - Wikipedia)。

言い換えると

最近接丸めの方式はISO 31-0やJIS Z 8401でも定められており,丸め方についての話はJIS規格の資料が分かりやすかったです。

日本工業標準調査会で「Z8401」と検索することでアクセスできます)

この資料では,

  • 「丸める」とは,与えられた数値を,ある一定の「丸めの幅」の整数倍がつくる系列から選んだ数値に置き換えること

であると整理した上で

  1. 与えられた数値に最も近い整数倍が1つしかない場合には,それを丸めた数値とする
  2. 与えられた数値に等しく近い,2つの隣り合う整数倍がある場合(端数が0.5の場合)には,偶数倍のほうを丸めた数値とする

という処理であると説明されています。

(また,2回以上繰り返して丸めると誤差の原因となるため,目的の桁の手前から1回だけ丸める(常に1段階で行う)という規則も書かれています。)

端数が5の場合,どっちの整数倍にも等しく近いから,そこでどうするかという部分で四捨五入とは異なるようです

f:id:nigimitama:20181130131008p:plain

round()は厳密に偶数丸めをするわけではない

偶数丸めのアルゴリズムから予想される結果と数値計算の結果との誤差

ニューヨーク大学のIEEE754についての講義資料に書かれていたIEEEの丸めの例が,以下のようなものでした

もとの数値 丸めた数値 理由
7.8949999 7.89 中間の値よりも小さいため
7.8950001 7.90 中間の値よりも大きいため
7.8950000 7.90 中間の値 - 最後の桁が偶数になるよう切り上げ
7.8850000 7.88 中間の値 - 最後の桁が偶数になるよう切り下げ

とくに最後の2つはちょうど中間にくる値を最後の桁が偶数になるようにしていて,上記のJISの説明とも整合します。

しかし,これをRで行うと

> x = c(7.8949999,7.8950001,7.8950000,7.8850000)
> df = data.frame(x, rounded = round(x,2))
> print(df, digits = 10)
          x rounded
1 7.8949999    7.89
2 7.8950001    7.90
3 7.8950000    7.89
4 7.8850000    7.88

3番目の結果が異なります。

例えば1.05や1.15を丸めた場合も,同様のことが起こります

> round(0.05, 1) # 偶数丸めになる
[1] 0
> round(0.15, 1) # 偶数丸めになる
[1] 0.2
> round(1.05, 1) # 偶数丸めにならない
[1] 1.1
> round(1.15, 1) # 偶数丸めにならない
[1] 1.1
> round(1.25, 1) # 偶数丸めになる
[1] 1.2
> round(1.35, 1) # 偶数丸めになる
[1] 1.4

representation errorに注意する必要がある

round()helpではこの誤差についても軽く触れられていて,representation errorに依るのだとか。

this is dependent on OS services and on representation error (since e.g.0.15 is not represented exactly, the rounding rule applies to the represented number and not to the printed number, and so round(0.15, 1) could be either 0.1 or 0.2).

(これはOSとrepresentation errorに依存します(例えば0.15は正確に表現されないため,丸めルールは表示される数字ではなくrepresented numberに適用されます。そのためround(0.15, 1)0.10.2のいずれかになります))

このあたりになってくると正直よくわからないんですが,

  • コンピュータは2進数で数値を処理しているため計算の過程で誤差が生じる
  • Rの場合,表示されている数字が「ぴったりその数字」ではないことがある(誤差の部分は表示を省略している)

ということが関係しているのだと思います。

後者は例えば,1.05を演算したあとの0.05がそのまま入力した0.05と一致しない,といった現象から察することができます。

> (1.05 - 1)
[1] 0.05
> (1.05 - 1) == 0.05
[1] FALSE
> (1.05 - 1) - 0.05
[1] 4.163336e-17

表示上はぴったり0.05であるものの,内部では 4.163336 \cdot 10^{-17}= 0.00000000000000004163336の誤差が生じているようです。

round()関数の内部を覗いたところ,(x - xの整数部)*丸め幅という計算をしてこれを丸める処理を行っている箇所がありました。(x - xの整数部)の計算,あるいは* 丸め幅の計算で「端数がぴったり5」にならなくなる値は,「2つの整数倍のちょうど中間の値」ではなくどちらかの整数倍に(誤差の分だけ)偏るため,丸め方が想定通りのものではなくなるようです。

参考

「統計的に有意」だけでは足りないワケ:バイアス-バリアンス分解のはなし

機械学習の教科書には,序盤などに「バイアスとバリアンス」とか「バイアス-バリアンス分解」といった項目があります。「誤差にはバイアスとバリアンスの2種類があるよ」という話です。正直私はそれを読んでも「ふーん。まぁ,そうだよね」と思うくらいで特に重要なトピックとも思っていませんでした。

ところが,統計学にも「バイアスとバリアンス」の話があって,そちらは個人的に「なるほど!!」となったのでここにメモしておきます。

バイアス(bias)とバリアンス(variance)

MSEの展開

推定したい真のパラメータ \thetaとその推定量 \hat{\theta}の平均二乗誤差(Mean Squared Error:MSE) E[(\hat{\theta} - \theta)^2]を分解(展開)すると,以下のようになります。

 
\begin{align}
MSE(\hat{\theta}, \theta)
&= E[(\hat{\theta} - \theta)^2] \\
&= E[(\hat{\theta} - E[\hat{\theta}] + E[\hat{\theta}]-\theta)^2] \\
&= E[(\hat{\theta} - E[\hat{\theta}])^2] + [E[\hat{\theta}]-\theta]^2\\
&= Var(\hat{\theta}) + Bias(\hat{\theta},\theta)^2
\end{align}

ここで Var(\hat{\theta})バリアンス(variance,普通に分散のこと), Bias(\hat{\theta},\theta)バイアス(bias)で,それぞれ

 
\begin{align}
Var(\hat{\theta}) &= E[(\hat{\theta} - E[\hat{\theta}])^2] \\
Bias(\hat{\theta},\theta) &= E[\hat{\theta}]-\theta
\end{align}

と定義されます。「パラメータの推定量と真の値との平均二乗誤差はバリアンスとバイアスの二乗の和である」ということです。

つまり?

「統計的に有意」かどうかの判断で使われる標準誤差(推定量標準偏差 SE(\hat{\theta})は分散 Var(\hat{\theta})平方根なので、ここに関わってきます。

上の分解は「パラメータの推定を誤る原因は,標準誤差とバイアスの2つに分けられる」ということであり,言い換えると,「『統計的に有意(分散が低い)』であっても推定が正しいとは限らない(バイアスが存在するかもしれない)」ということになります。これは非常に興味深いトピックです。

(ただし,標本平均のように不偏推定量(unbiased estimator)と呼ばれるタイプの推定量はバイアスがゼロなのでこの心配はありません。一方,回帰分析の最小二乗推定量などは不偏性を得るための前提が満たされにくいので問題になります。)

イメージ図

射的に例えてイメージ図を描くなら,以下のような感じでしょうか。的の中心が真の値 \thetaで,着弾点が推定量 \hat{\theta}です。

「バリアンスが低い(統計的に有意である)」ことが必ずしも「真の値をよく推定できている」と言えるとは限らないことがわかるかなと思います(右上の図)。

バリアンスを下げるには

「バリアンスを下げるにはどうしたらいいのか」を考えるために,まず,「標準誤差」ってなんだっけ,「統計的に有意」ってなんだっけ,というところの用語の確認から行っていきます。回帰分析での推定を例に考えていきます。

標準誤差

標準誤差(standard error)はパラメータの推定量と真の値とのばらつき(標準偏差)のことです。

回帰分析の場合,誤差項 \varepsilon_iの分布について

 
\varepsilon\_i \sim N(0, \sigma^2)

という仮定をおくと,傾き係数 \hat{\beta_1}の分布は

 
\hat{\beta\_1} \sim ~ N\left(\beta\_1, \frac{\sigma^2}{\sum^n\_{i=1}(X\_i-\bar{X})^2}\right)

となることが知られています。

誤差項 \varepsilon_iの分散 \sigma^2の不偏推定量 \hat{\sigma}^2は,回帰分析の残差 \hat{u}_i = Y_i - (\hat{\beta}_0+\hat{\beta}_1X_i)から得られることが知られていて,以下のようになります。

 
\hat{\sigma}^2 = \frac{\sum\_{i=1}^n\hat{u}\_i}{n-2}

これを用いると,推定量 \hat{\beta}_1の標準誤差は

 
SE(\hat{\beta}\_1) = \sqrt{\frac{\hat{\sigma}^2}{\sum^n\_{i=1}(X\_i-\bar{X})^2}}

となります。

「統計的に有意」かどうかは, t検定の場合,推定量(と帰無仮説で想定する真の値の差)を標準誤差で割って得る t値(推定量のばらつきに対する推定量の大きさ)から判断します。

バリアンスを下げるには

バリアンスを下げるには,どうすればよいのでしょうか。

標準誤差 SE(\hat{\beta}_1)は,言い換えれば

 
SE(\hat{\beta}\_1) = \sqrt{\frac{\hat{\sigma}^2}{\sum^n\_{i=1}(X\_i-\bar{X})^2}} = \sqrt{\frac{\text{誤差項のばらつき}}{\text{データのばらつき}}}

で,「誤差項のばらつき」 \hat{\sigma}^2

 
\hat{\sigma}^2 = \frac{\sum\_{i=1}^n\hat{u}\_i}{n-2} = \frac{\text{予測精度の低さ}}{\text{サンプルサイズの大きさ}}

になります。つまり,バリアンスを下げるには,

  • サンプルサイズを上げて \hat{\sigma}^2を下げる
  • 予測精度を上げて残差 \hat{u}_iを下げる

という方法が考えられます。

例えば手元にモデルに入れていない変数がある場合に投入してみて残差を減らしてみたり,とかです。

バイアスを下げるには

「バイアスを回避するにはどうしたらよいのか」,あるいは「そもそもどういうときにバイアスが発生するのか」といったことについて考えてみます。

バイアスの種類と原因

1.欠落変数バイアス(除外変数バイアス)

本来なら説明変数としてモデルに含まれるべき変数が欠落しているために誤差項と説明変数が独立でなくなり生じるバイアスを欠落変数バイアス(omitted variable bias)と呼びます。

例えば X\to Yへの因果関係を見たいとき, X,Yの両方に影響を与える変数があるとき,これを交絡変数(confounding variable)と呼びます(交絡因子とも呼ばれます)。交絡変数があるときは,モデルに含めないと欠落変数バイアスが生じます。

f:id:nigimitama:20210227162055p:plain

2.同時決定バイアス

被説明変数と説明変数が相互に影響を与え合っている(因果関係がループしている)関係にあるときもまた誤差項と説明変数が独立でなくなり,バイアスを生みます。

f:id:nigimitama:20210227162000p:plain

3.内生性バイアス(逆の因果性)

説明変数に使用している変数が内生変数(想定しているモデルの中で内生的に決定される変数で,被説明変数に該当する)である場合も誤差項と説明変数が独立でなくなり,バイアスを生じます。

f:id:nigimitama:20210227162042p:plain

バックドア基準

バイアスを避けるには,データが生成されている構造をしっかりと捉えて,上記のバイアスを生むことがないように統計モデルを構築する必要があります。その際に参考になる変数選択基準がバックドア基準というものです。

バックドア基準

原因をX,結果をYと表すとき, 1. 追加した説明変数はXの子孫(下流側)ではない 2. (Xから出る矢印を除いたときの因果構造において) 追加した説明変数によってXとYの交絡因子からの流れをすべて遮断できている

バックドア基準については林岳彦先生のスライドや岩波データサイエンスVol.3での記事がわかりやすいのでおすすめです(岩波の記事はこのスライドと中身ほぼ一緒です)。

『バックドア基準の入門』@統数研研究集会 - SlideShare

今回の話から導かれる推定の3つの方向性

(※この先の記述は他に明示している文書を見たことがないので私の推測です)

「『統計的に有意』であればその推定が正しいわけではない」という点から生まれる「ではどう推定していくべきか」についての考え方は,私が知る範囲では3つあります。

1.実験(ランダム化比較試験・A/Bテスト)

実験(ランダム化比較試験:効果を測定したい処置を行うグループへの被験者の割付をランダムに行う)によってデータの生成構造をコントロールし,バイアスを回避するアプローチです。

2.準実験・擬似実験

計量経済学では「実験(ランダム化比較試験)を行いたいが,分野の都合上,実験は難しい」という分野の特性に合わせて,観測データから「実験に似た状況(ランダム割付が発生している状況)を探し出し,実験したとみなす」ような手法をとることもあります。

実験と見なせる状況は観測されたデータのうちごく一部になってしまうので,局所的(一部の主体への)平均因果効果の推定になってしまうことも多いですが,「ランダム化されているのでバイアスは除去できている」という点は分析上強い武器になります。

3.統計モデリング

現実のデータ生成構造をうまく模すことができない統計モデルを組んで推定を行うと,パラメータにバイアスが含まれます。なので,データを良く見て正しい統計モデリングを行おうという話です。

計量経済学では実験的なアプローチが導入される前のパラダイムではこの方向性が主流でした。しかし,例えばバイアスを生む原因となる要因が観測不可能なものである場合に対処がきわめて難しくなるなど,限界はあります(例えばその人の「知性」や「健康への関心の高さ」という明示的にデータにできないものが交絡因子であると考えられるときにどう統計モデルに組み込むか)。

まとめ

  • パラメータの真の値と推定量の誤差(MSE)は,バイアスとバリアンスに分けられる
  • 「バリアンスが小さい(統計的に有意)」かつ「バイアスがない」推定量が良い推定量
  • バイアスの回避のためには,いろいろアプローチがあり,できれば実験が望ましい

参考

ディスプレイは2~3個あると作業が捗る説

今年に入ってからディスプレイの数を1台から2台へ,2台から3台へと段階的に増やしました。

その結果,すごく作業が捗るようになりました。仕事でも遊びでも。

世の中には「ノートPCひとつあれば十分」という方も多いとは思うのですが,私の体験を述べてみたいと思います。

f:id:nigimitama:20181116203449p:plain

ディスプレイが増えると何が嬉しいのか

ディスプレイを増やすメリットは,当たり前ですが情報を表示させることができる領域が増えることです。

例えば

  1. scikit-learnのDocumentationを参照しながら
  2. pythonのコードを書き,
  3. その隣で今コードを書いている機械学習手法の理論の文献を参照しながら
  4. 横で動画を再生してチラチラ見たり見なかったりする

といった感じに,いろんなことを同時並行できます。

とくに,コードを書きながら目を隣に移すだけでDocumentationを参照できると捗ります。いちいちウィンドウを切り替えるのは手間ですから。

どんなディスプレイがいいのか

ディスプレイは横置き,縦置き,アーム固定といった置き方があります。

大抵のディスプレイは横置き用ですが,一部のディスプレイは画面を90度回転させて縦置きできるようになっています(画面を回転できることはピボット機能と呼ばれるようです)。

縦置きディスプレイは

  1. コードが書きやすく,読みやすい
  2. 文書・電子書籍を読みやすい
  3. デスクの横幅をとらない

といったメリットがあるので,デュアルディスプレイにするときはピボット機能のあるディスプレイを買って,上の画像のようにメインのディスプレイを横置きにしてサブを縦置きにする構成がおすすめです。私がデュアルディスプレイにしていたときもこの構成でした。

ちなみに,コードも文書も読まないときはgoogle chromeのウィンドウを2つ開いて上下に並べています。これが意外と便利です。

ピボット機能のあるディスプレイ

私はiiyamaを使っています。iiyamaはピボット機能のあるディスプレイを出していることで有名なメーカーです。

同じくらいの価格帯でI-O DATAも出しています。

価格.comでは「ピボット機能」でフィルターを掛けて検索できるので,ここで検討するのも良いと思います。

kakaku.com