基本情報技術者試験 IDS

問題

IDSの機能はどれか。

ア PCにインストールされているソフトウェア製品が最新のバージョンであるかどうかを確認する。
イ 検査対象の製品にテストデータを送り,製品の応答や挙動から脆(ぜい)弱性を検出する。
ウ サーバやネットワークを監視し,侵入や侵害を検知した場合に管理者へ通知する。
エ 情報システムの運用管理状況などの情報セキュリティ対策状況と企業情報を入力し,組織の情報セキュリティへの取組み状況を自己診断する。

解説


IDSの解説


IDS(Intrusion Detection System,侵入検知システム)は、
ネットワークやホストをリアルタイムで監視し、
異常を検知した場合に管理者に通知するなどの処置を行うシステムです。
したがってIDSの機能についての記述は「ウ」です。

異常を通知することを目的としたシステムのため通信の遮断などの
防御機能を持たないことがほとんどです。

IDSその用途から、
ネットワークセグメントに接続しネットワークをの通信を監視する
NIDS(Network-Based IDS)」と、
監視対象のサーバ(ホスト)にインストールして
そのサーバで発生するイベント(受信データやログ)を監視する
HIDS(Host-Based IDS)」の2つに分類されます。

不正の検出方法には「シグネチャ型」「異常検出型」の2種類があります。
シグネチャ型はあらかじめ登録した、シグネチャ(不正な通信の特徴情報)に基づき、
一致した場合に不正と判断します。
そのため、登録されていないパターンの不正な通信は検出できません。

異常検出型は、あらかじめ監視対象の正常時の状態を登録することで、
その状態に反した場合、不正と判断します。
そのため異常検出型のように「特徴を追加する」必要がなく、未知の攻撃を検出できます。なお、多くのIDSではシグネチャ型が採用されています。

IPSの解説

IDSと似たような名前のものにIPSがあるので解説します。
IPS (Intrusion prevention system, 侵入防止システム)では検知した不正を自動的に遮断します。

IDSが「検知(Detection)」するのに対して、IPSは侵入を未然に「防止(Prevention)」するできるから、IDSより優れているっていう認識で大丈夫だと思います。

IDSとIPS、2つあわせてIDPSという場合もあります。

Intrusionを使った熟語


IDSとIPSについてはIntrusionを覚えておけばよさそうなので熟語で覚えましょう。
  • unwarranted intrusion 不法侵入 
  • intrusion alarm 侵入警報
  • intrusion alert 侵入警報
  • intrusion attack 侵入攻撃
  • an intrusion on other people's privacy 他人のプライバシーの侵害

他の選択肢の解説


ア PCにインストールされているソフトウェア製品が最新のバージョンであるかどうかを確認する。
→バージョンチェックツールの説明です。
イ 検査対象の製品にテストデータを送り,製品の応答や挙動から脆(ぜい)弱性を検出する。
→ファジングの説明です。ファジングとは、
検査対象のソフトウェア製品に「fuzz」と呼ばれる問題を引き起こしそうなデータ
を送り込み、潜在的なバグ・脆弱性を検出する手法です。
エ 情報システムの運用管理状況などの情報セキュリティ対策状況と企業情報を入力し,組織の情報セキュリティへの取組み状況を自己診断する。
→情報セキュリティ対策ベンチマークの説明です。

参考文献



基本情報技術者試験 個人情報保護法

問題 

個人情報保護法が対象としている個人情報はどれか。

ア 行政機関に登録されている個人に関する情報に限られる。
イ 個人が秘密にしているプライバシに関する情報に限られる。
ウ 生存している個人に関する情報に限られる。
エ 日本国籍の個人に関する情報に限られる。

解説

個人情報保護法の定義

個人情報保護法が対象としている個人情報については,第2条に,規定されています。
以下に、一部抜粋してのせておきます。

(定義)
第二条 この法律において「個人情報」とは、生存する個人に関する情報であって、次の各号のいずれかに該当するものをいう。
一 当該情報に含まれる氏名、生年月日その他の記述等(文書、図画若しくは電磁的記録(電磁的方式(電子的方式、磁気的方式その他人の知覚によっては認識することができない方式をいう。次項第二号において同じ。)で作られる記録をいう。以下同じ。)に記載され、若しくは記録され、又は音声、動作その他の方法を用いて表された一切の事項(個人識別符号を除く。)をいう。以下同じ。)により特定の個人を識別することができるもの(他の情報と容易に照合することができ、それにより特定の個人を識別することができることとなるものを含む。)
二 個人識別符号が含まれるもの
2 この法律において「個人識別符号」とは、次の各号のいずれかに該当する文字、番号、記号その他の符号のうち、政令で定めるものをいう。
一 特定の個人の身体の一部の特徴を電子計算機の用に供するために変換した文字、番号、記号その他の符号であって、当該特定の個人を識別することができるもの
二 個人に提供される役務の利用若しくは個人に販売される商品の購入に関し割り当てられ、又は個人に発行されるカードその他の書類に記載され、若しくは電磁的方式により記録された文字、番号、記号その他の符号であって、その利用者若しくは購入者又は発行を受ける者ごとに異なるものとなるように割り当てられ、又は記載され、若しくは記録されることにより、特定の利用者若しくは購入者又は発行を受ける者を識別することができるもの
・・・・・
4 この法律において個人情報について「本人」とは、個人情報によって識別される特定の個人をいう。
5 この法律において「仮名加工情報」とは、次の各号に掲げる個人情報の区分に応じて当該各号に定める措置を講じて他の情報と照合しない限り特定の個人を識別することができないように個人情報を加工して得られる個人に関する情報をいう。
・・・・・
6 この法律において「匿名加工情報」とは、次の各号に掲げる個人情報の区分に応じて当該各号に定める措置を講じて特定の個人を識別することができないように個人情報を加工して得られる個人に関する情報であって、当該個人情報を復元することができないようにしたものをいう。
・・・・・
7 この法律において「個人関連情報」とは、生存する個人に関する情報であって、個人情報、仮名加工情報及び匿名加工情報のいずれにも該当しないものをいう。
・・・・・


定義に
「個人情報」とは、生存する個人に関する情報であって
とあるので、正解はウです。

法律なので分かりにくく感じますが、例をあげると、
名刺はそれ1枚で個人を特定できる個人情報であると解釈されます。
また、公開企業の代表者の住所や氏名などの個人情報は、
不特定多数に向けて公開されている情報であっても法律による保護の対象となります。
このため、公開されていても、本人の許諾なしに個人情報を使用すれば
法律に違反する可能性があります。

また、個人情報は、個人を特定できる情報なので、
匿名に加工されている利用者アンケート情報などは、
個人を特定できないため、個人情報に該当しません。

他の選択肢の解説


ア 行政機関に登録されている個人に関する情報に限られる。
→個人情報保護法の対象は、行政機関だけではなく、
 民間の個人情報取扱事業者も対象です。
イ 個人が秘密にしているプライバシに関する情報に限られる。
→プライバシに関する情報はもちろんですが、
 名刺に記載されている内容も個人情報となります。
エ 日本国籍の個人に関する情報に限られる。
→個人情報は、生存する個人に関する情報ということだけで、
 外国人か日本人かについては,規定されていません。

参考文献


基本情報技術者試験 XP(eXtreme Programming)

基本情報技術者試験にて、
XP(eXtreme Programming)において,プラクティスとして提唱されているものはどれか。
という問題が過去に出題されたので、XPについて解説します。

XP(eXtreme Programming)とは

XP(エクストリームプログラミング, eXtreme Programming)とは、
アジャイル開発における開発手法の1つです。

アジャイル開発とは、最初から綿密な計画を立てずに、臨機応変に進める開発方法です。
設計・実装・テストを短期間で何度も繰り返しながら、
顧客の意見や要望を都度取り入れて開発を進めます。
設計・実装・テストの1サイクルをイテレーションと呼びます。

アジャイル開発には指針となる手法があります。有名なのがスクラムです。
XPはスクラムほどではありませんが、アジャイル開発手法として知られています。
スクラムが勢いよく製品を作る手法であるのに対し、
XPは継続的な成長に主眼を置いています。

XPの5つのvalues

XPでは、以下の5つのvaluesが提唱されています。

  • コミュニケーション
  • シンプル
  • フィードバック
  • 勇気
  • 尊重

XPのプラクティス


XPでは、共同プラクティス・開発プラクティス・管理者プラクティス・顧客プラクティス
に従って開発を進めます。

開発プラクティス

開発プラクティスがXPの特色といえるので、より詳細に解説しておきます。
いくつかのプラクティスに分けられますが、代表的なものだけを紹介します。

テスト駆動開発

テスト駆動開発では、プログラムの実装よりもテストコードを先に作成します。
それにより、求められる機能が洗い出され、シンプルな設計が実現します。

テストの通過を目標に開発を行えば、仕様変更による開発途中のブレや本番環境でのバグの数を最小限に抑えられるでしょう。

ペアプログラミング

ペアプログラミングは、2人1組でプログラミングを行うことです。
1人がコードを記述し、もう1人はそれを確認・補佐します。

この手法の背景にある考え方は、1人の頭脳と2人の目よりも、
2人の頭脳と4人の目の方が優れているというものです。
記述しながらのコードレビューが可能になり、
一人の開発者では止まってしまうような厄介な問題にも迅速に対応できます。

リファクタリング

リファクタリングとは、完成したコードをわかりやすく書き換えることです。
同じ動作をするコードでも、シンプルにすることで、
メンテナンス性の向上や不具合の発生頻度の低下が期待できます。
最も推奨されるリファクタリングの使い方の1つは、プロセスの重複の排除です。

参考文献


基本情報技術者試験 誤り制御の実施方法

この記事では、基本情報技術者試験を受けようとされている方に向けて、
誤り制御の実施方法に関する内容の解説します。

基本情報技術者試験では比較的出題例が多い分野となろので、
しっかり勉強しておきましょう。

誤り制御とは

コンピュータ間通信の途中で失われた情報や壊れた情報を検知し、適切な状態に修正することを指します。コンピュータ間通信では必ずエラーが発生するため、誤り制御が必須となります。

誤り制御の手法


以下では、試験で出題されたことがある各種のメモリの誤り制御方式を解説します。

パリティチェック


パリティチェックは、データに対してパリティビットと呼ばれる検査用のビットを追加することで誤りを検知する方法です。発生する主な誤りは、データビット列中の0と1が誤って送付されることであるため、パリティビットによりデータビット列の0と1の組み合わせが正しいものであるかを確認する手法が有効です。

パリティチェックはその方法により、奇数パリティと偶数パリティ、および垂直パリティと水平パリティに分かれます。

奇数パリティと偶数パリティ


対象となるビット列の1の個数が奇数である場合にパリティを0にするのが奇数パリティであり、対象となるビット列の1の個数が偶数である場合にパリティを0にするのが偶数パリティです。

これらの方法では1ビットの誤りは偶奇が逆になるので検出できますが,ビット位置の特定はできません。また,2ビット誤ると偶奇が誤る前と同じになるため,誤りの検出はできません。

垂直パリティと水平パリティ


垂直パリティはデータのビット列を一定ブロックごとに分けて並べたうえで、
垂直方向に対してパリティビット(奇数パリティか偶数パリティ)を設ける方法です。
水平パリティは同様にビット列を一定ブロックごとに分けて並べたうえで、
水平方向に対してパリティビットを設ける方法です。

どちらか1つの方法だけであれば,奇数パリティと同じように
偶奇の違いで誤りを検出できません。

水平パリティと垂直パリティを併用することによって,
1ビットの誤りと訂正ができるようになります。

チェックサム


各項目の合計値をたし,その結果をデータとともに記録しておきます。データを読み出すときも,同じ計算をし,合計と違っていたら,データが誤って記録されたと判断できます。

ハミング符号方式


ハミング符号方式では、2ビットの誤りを検出でき,1ビットの誤りは訂正できます。

パリティビットを複数つけることによって、誤りを検知できる量を増やします。

パリティチェックでは、ビット列に誤りの有無しかわかりませんでしたが、ハミング符号方式では複数のパリティビットによりデータのどこに誤りが生じたかまで特定できます。これによって、誤り検知時に再送要求をする必要がなく、受信側でデータを訂正でき、効率的な通信を実現できます。

CRC(Cyclic Redundancy Check, 巡回冗長検査)


CRCは、ビット列を生成多項式で割った余りを算出して相手に送付ことによって、パリティチェックなどの方式よりも多くのビットの誤りを検知できるようにした方法です。

CRC符号は誤り検出用の符号であり、正しい値へ訂正する機能はありません。

参考文献

基本情報技術者試験 アクチュエータ

問題

アクチュエータの機能として,適切なものはどれか。

ア キーボード,タッチパネルなどに使用され,コンピュータに情報を入力する。

イ アナログ電気信号を,コンピュータが処理可能なディジタル信号に変える。

ウ コンピュータが出力した電気信号を力学的な運動に変える。

エ 物理量を検出して,電気信号に変える。

解説


コンピュータが出力した信号を機械的な動作に変えるものをアクチュエータ(作動装置)と総称します。なのでウが正解です。

actuate(作動させる)にor(するもの)をくっつけたのが、アクチュエータなので、
直訳すると作動させるものとなります。

直訳通り、アクチュエータとはエネルギー供給によって回転運動を行ったり、精密制御を加えたり等機器を動かすためのデバイスです。

動力源+動力源を任意の信号へと変換する回路+機械的要素を備えています。

動力源には電気や油圧などが使われています。

アクチュエータの使用例

  • ワイパーの往復運動
  • ドアの開閉
  • ロボットの関節動作
  • 医療機器
  • ゲームのコントローラの振動   等

他の選択肢の解説

ア キーボード,タッチパネルなどに使用され,コンピュータに情報を入力する。
→ 入力デバイス

イ アナログ電気信号を,コンピュータが処理可能なディジタル信号に変える。
→ アナログ-デジタル変換回路

エ 物理量を検出して,電気信号に変える。
→ センサー

参考文献

JavaScript 論理演算子 AND OR

本記事では論理演算子のAND、OR演算子を紹介します。

論理演算子

論理演算子は基本的に真偽値を扱う演算子です。

AND演算子は両辺の演算子がどちらも、trueであればtrueを返し、そうでなければfalseを返す、OR演算子は両辺の演算子がどちらも、falseであればfalseを返し、そうでなければtrueを返すという演算子です。

AND演算子(&&)

AND演算子(&&)は、左辺の値の評価結果がtrueに変換できるならば、
右辺の評価結果を返します。
一方で、左辺の値の評価結果がfalseに変換できるならば、
右辺は評価されること無く、左辺の値がそのまま返されます。

このような値が決まった時点でそれ以上評価しないような評価のことを
短絡評価(Short-circuit evaluation)と呼びます。

// 左辺はtrueであるため、右辺の評価結果を返す
console.log(true && true); //true
console.log(true && false); //false
// 左辺がfalseであるなら、その時点でfalseを返す。 右辺は評価されない
console.log(false && true); //false
console.log(false && false); //false

また、AND演算子は左辺を評価する際に、左辺を真偽値へと暗黙的な型変換をしてから判定します。 真偽値への暗黙的な型変換ではどの値がtrueでどの値がfalseになるかは、次のルールによって決まります。

falsyな値はfalseになる

falsyでない値はtrueになる

falsyな値とは以下の値のことを言います。

  • false
  • undefined
  • null
  • 0
  • -0
  • 0n
  • NaN
  • ""や''や``(空文字列)

trueへと変換される値の種類は多いため、falseへと変換されない値はtrueとなると考えておけば十分だと思います。 このオペランドを真偽値に変換してから評価するのはAND、OR演算子で共通の動作です。

次のように、AND演算子(&&)は左辺を真偽値へと変換した結果がtrueの場合は、右辺をそのまま返します。 左辺がfalsyの場合は、右辺は評価されず、左辺がそのまま返されます。

// 左辺はfalsyではないため、評価結果として右辺を返す
console.log(true && 'hello'); //hello
console.log('other' && 'hello'); //hello
// 左辺がfalsyであるため、評価結果として左辺を返す
console.log(false && 'hello'); //false
console.log(undefined && 'hello'); //undefined
console.log(null && 'hello'); //false
console.log(0 && 'hello'); //0
console.log(-0 && 'hello'); //-0
console.log(0n && 'hello'); //On
console.log(NaN && 'hello'); //NaN
console.log("" && 'hello'); //空文字列

AND演算子の利用例

論理演算子は、if文と組み合わせて利用することが多い演算子です。

例1

次のように、numberが3で割り切れかつ 5で割り切れる場合という条件をひとつの式として書くことができます。

let number = 15;
if (number % 3 == 0 && number % 5 == 0) {
    console.log(number);
}

// if文のネストで書くと以下のようになる let number = 15; if (number % 3 == 0) {     if (number % 5 == 0) {         console.log(number);     } }
//出力 15

AND演算子(&&)を使うと、if文のネストに比べて短く書くことができます。

なお、このときに、numberが3で割り切れない(例えば5等の)場合は、その時点でif文の条件式はfalseとなります。 そのため、AND演算子(&&)の右辺は評価されずに、if文の中身も実行されません。

例2

let password = "123456";
if (password  && password.length < 8) {
    console.log('パスワードは8文字以上で入力してください。');
}

この例では、passwordのlengthプロパティを確認し、8よりも小さければエラーメッセージを設定しています。

変数passwordに格納された文字列の長さを見ています。

なので、やりたいことだけを書くと、以下のようなプログラムになりそうです。

if (password  && password.length < 8) {
    console.log('パスワードは8文字以上で入力してください。');
}

ただし、この書き方はよくないです。

これはJavaScript以外の言語にもいえますが、
オブジェクトのpropertiesを参照する時には、そのオブジェクト自体が
nullやundefinedである可能性を考慮しなければいけません。
(厳密な言い方をすると、nullやundefinedはプリミティブだから、
propertiesを持てないのですが、本記事では詳細を割愛します。)

つまり、以下のような書き方をした時に、
エラーが発生し、プログラムが終了してしまいます。

let password;
if (password  && password.length < 8) {
    console.log('パスワードは8文字以上で入力してください。');
}

// 出力 // Error evaluating Javascript output:  TypeError: Cannot read properties of undefined (reading 'length')

上記の例ではpasswordは宣言されただけで、何も値が代入されていません。

そのため変数passwordの中身はundefinedになっているのですが、

if分の中ではpassword.lengthという形で、passwordのpropertiesを呼び出しています。

当然undefinedにはlengthという名前のpropertiesはありません。

そのため、エラーが生じてしまうのです。

それを防ぐために、

password &&

という記載を追加します。

つまり、passwordに値が代入されていない場合は、
エラーが発生するプログラムが実行される前に、処理を打ち切ります。

passwordの中身がnullやundefinedだった場合には、
AND演算子の左側の値がfalseであるとみなされ、その時点で処理が打ち切られるので、
エラーが発生するpassword.lengthという処理が実行されません。

この書き方はよく用いられます。

OR演算子(||)

OR演算子(||)は、左辺の値の評価結果がtrueに変換できるならば、そのまま左辺の値を返します。 一方で、左辺の値の評価結果がfalseならば、右辺の評価結果を返します。

// 左辺がtrueなので、左辺の値が返される
console.log(true || true); //true
console.log(true || false); //true
// 左辺がfalseなので、右辺の値が返される
console.log(false || true); //true
console.log(false || false); //false

また、AND演算子と同様に、OR演算子は左辺を評価する際に、左辺を真偽値へと暗黙的な型変換します。 次のように、OR演算子は左辺がfalsyの場合には右辺の値を返します。

// 左辺はfalsyではないため、左辺の値が返される
console.log(true || 'hello'); //other
console.log('other' || 'hello'); //other
// 左辺がfalsyなので、右辺の値が返される
console.log(false || 'hello'); //hello
console.log(undefined || 'hello'); //hello
console.log(null || 'hello'); //hello
console.log(0 || 'hello'); //hello
console.log(-0 || 'hello'); //hello
console.log(0n || 'hello'); //hello
console.log(NaN || 'hello'); //hello
console.log("" || 'hello'); //hello

OR演算子の利用例

例1

今までは1つの式に1つの演算子を使った例だけを紹介しましたが、多数の演算子を用いることができます。
let firstName = "";
let lastName = "mue"; let userName = "駆け出しエンジニア"; const name = firstName || lastName || userName || "Anonymous";
console.log(name); 
// 出力 mue

上記の例では、firstName、lastName、userNameの順に値を確認していき、trueに変換できるものがあれば、それ以降は評価されず、その値が出力されます。変換できるものが無ければ、Anonymousが出力されるようになっています。

例2

let obj = { test: 'test' };
obj = obj || {};
console.log(obj); 
 //出力 { test: 'test' } 
let obj ;
obj = obj || {};
console.log(obj); 
 //出力 {}

上記2つのコードの2行目をみてください。obj が未定義ならば、obj を生成され、すでに存在してる場合は、そのまま使うということをやっています。

なぜこんな紛らわしい書き方をするかというと、AND演算子の使用例でも述べたように、
オブジェクト自体がnullやundefinedである可能性を考慮しなければいけないからです。

変数に自分自身を代入する読みにくい変なコードなのですが、
初期値を指定する際などに、非常によく使われる書き方なので覚えておきましょう。

参考文献

・ITやWebに関する備忘録 IT / Web技術. JavaScriptのANDとOR【現場でよく使われる特殊な書き方】. <https://web.lingual-ninja.com/2019/07/javascript-and-or.html> (参照日2022年10月8日).
・JavaScript Primer. 演算子. <https://jsprimer.net/basic/operator/> (参照日2022年10月8日).
・ゼロプラスワン. JavaScript 論理演算子OR「||」の特殊な用法. <https://zero-plus-one.jp/javascript/javascript-logical-operators-or/> (参照日2022年10月8日).

Windows でnpmのnode_modulesを削除する方法

Windows11にローカルでインストールしたnode_modulesを
エクスプローラー経由での削除ができなくて困っていたので、
node_modulesを削除方法をメモとして残しておきます。

windowsでnode_modulesを削除ができない原因

結論からいうと、パスの長さが250文字以上をデフォルトで認識しないようになっているからです。

npmでは依存ライブラリをnode_modulesというディレクトリの中に保存しています。
ライブラリhogeが依存しているライブラリはnode_modules/hoge/node_modules/
の中に保存されます。この方式をとることで、使っているライブラリAとBが、
共通のライブラリCに依存している場合に、AとBが使っているCのバージョンが
異なっていても問題無くなります。

その代償として、ファイル数が増えています。
また、依存関係が深ければ深いほどファイルシステム上のパスも長くなっています。

LinuxやMacではパスが長くても問題ありません。
しかし、Windowsでは長いパスは、OSやファイルシステムは対応してますが、エクスプローラーやコマンドプロンプトが対応してません。

要するに、Windowsでは、ファイルパス名が長すぎて削除できないモジュールが
出てくるため、node_modules ディレクトリを削除できませんでした。

削除方法

削除方法としては、フォルダの共有化で一括削除する方法やnpm uninstall パッケージ名
で削除する方法等があるようですが、本記事ではより簡潔な方法を紹介します。

①windowsアプリのubuntuを立ち上げます。

②消去したいnode_modulesディレクトリまでcdコマンドで移動します。

③以下のコマンドを実行します。

rm -r node_modules

 rm(ファイルを削除する)コマンドに、オプションとして
 -r(ディレクトリを削除)をつけています。   

エクスプローラーは対応していないけれど、OS自体は対応しているので、
以上の方法で削除できるかと思います。 

参考文献

・Qiita @ryomoucmei. Windows10でnode_modulesをサクッと消す手順. <https://qiita.com/ryomoucmei/items/2b7c9494316976217ae7> (参照日2022年10月1日).
・Qiita @kusano_k. Windows 10でnpmのnode_modulesを削除する方法. <https://qiita.com/kusano_k/items/570b6442780538b83a41> (参照日2022年10月1日).
・Monaural. Windows環境下でローカルインストールされたnpmモジュールを一括削除する. <https://ka2.org/delete-local-installed-npm-modules-into-windows> (参照日2022年10月1日).

Linuxコマンド sudo su とは

本記事では、LinuxOSを操作するときに出てくる sudo su というコマンドを解説します。

sudoコマンド(Super User Do)とは?

スーパーユーザー(=root)としてsudoに続くコマンドを実行する権限を
その都度与えるコマンドです。

つまり、その瞬間だけroot権限(Windows系OSでいうとAdministrator)
になってsudoに続くコマンドを実行するコマンドです。

コマンド実行完了後は権限が元に戻ります。

root権限は、システム管理者用のアカウントに付与されるほぼすべての
操作ができる強力な権限なので、sudoを使う際には注意が必要です。

#管理者として実行したいコマンドがある場合に使う
sudo 実行したいコマンド

suコマンドとは?

sudoの続きによくくる「su」というのはコマンドです。

suコマンド(switch user)はユーザを切り替えるコマンドになります。

このsuコマンドを実行すると以降の操作は切り替えたユーザで操作になります。

#ユーザを切り替えたいときに使う。
su 切り替えるアカウント名

suコマンドでユーザを省略するとrootを切り替えたことになります。

つまり、sudo suコマンドはroot権限でsuコマンドを実行します。(sudo su rootと同じ)

sudo suとsudo su -の違い

suコマンドに-(ハイフン)がついているときとそうでないときがあります。

ハイフンがついているときは、ホームディレクトリが
切り替えたユーザのホームディレクトリに移動します。

#ユーザーを切り替え+切り替えたユーザーのホームディレクトリへ移動
su - 切り替えるアカウント名

つまり、sudo su - ではホームディレクトリがrootに移動します。

そのため、カレントディレクトリを切り替えたくない場合はハイフンは付けません。

参考文献

・Qiita @msht0511. 「sudo su」ってざっくり言ってなんぞ?. <https://qiita.com/msht0511/items/31294277a4415ccb4ac9> (参照日2022年9月24日).
・株式会社CONFRAGE ITソリューション事業部. Linuxコマンドのsudoとsuとsudo suとsudo su -コマンドの違いを分かりやすく. <https://confrage.jp/linux%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AEsudo%E3%81%A8su%E3%81%A8sudo-su%E3%81%A8sudo-su-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E9%81%95%E3%81%84%E3%82%92%E5%88%86%E3%81%8B%E3%82%8A/> (参照日2022年9月24日).

Windows 「desktop.ini」がデスクトップ上に表示される場合と起動時に勝手に開かれる場合の対処方法

Windowsで、「desktop.ini」がデスクトップ上に表示される場合と起動時に勝手に開かれる場合の対処方法を紹介します。私もこの不具合に遭遇して解決方法を調べたのですが、よく紹介されている方法では解決せず困っていため、忘備録として残しておきます。

desktop.iniとは?

デスクトップ画面の構成情報を保存しているシステムファイルです。desktop.iniにはデスクトップアイコンやフォルダ名・表示情報が記述されています。

desktop.iniはシステムファイルなので基本的には非表示設定されています。
しかし、フォルダーオプションの設定が変更されてシステムファイルが表示されるようになった場合に、このdesktop.iniファイルもデスクトップ画面に表示されたり、起動時に勝手に開かれたりするようになります。

desktop.iniは削除しても大丈夫?

一応削除すれば不具合は解決するのですが、削除してしまうとデスクトップの構成情報が参照できなくなり、表示が崩れたり、英語表記になったりするので、注意しましょう。

そこで、システムファイルの表示設定を初期状態に戻すことで、「desktop.ini」ファイルを非表示にする方法をおすすめします。

desktop.iniの非表示方法

  1. 「エクスプローラー」を起動
  2. フォルーダオプションの「表示」タブを選択
  3. 詳細設定の「隠しファイル、隠しフォルダー、または隠しドライブを表示しない」と「保護されたオペレーティング システムファイルを表示しない(推奨)」
    のチェックを入れ、「OK」をクリック

以上の方法が、本記事の不具合の解消方法としてよく他のサイトでも紹介されていました。

私の場合はこの方法では非表示にならず、以下の方法で解決しました。

PowerShellを開き、以下のコマンドを打ち込んでください。

attrib +s +h +r /s desktop.ini

現在のディレクトリとそのすべてのサブディレクトリ内にある(/s)、desktop.iniにシステムファイル属性(s)、隠しファイル属性(h)、読み取り専用ファイル属性(r)をattribコマンドで設定する(+)という仕掛けです。

参考文献

・Aprico. desktop.iniとは?削除しても大丈夫?非表示方法を紹介!. <https://aprico-media.com/posts/3875> (参照日2022年9月3日).
・microsoft. Docs/Windows Server/Windows のコマンド/関連項目/attrib. <https://docs.microsoft.com/ja-jp/windows-server/administration/windows-commands/attrib> (参照日2022年9月3日).

Blogger Simpleテーマのヘッダー(ブログタイトルや説明)をカスタマイズ

BloggerでSimpleテーマを利用している方向けに、
ヘッダーをカスタマイズする方法を紹介します。ヘッダーをカスタマイズすることで、
ブログタイトルやブログの説明の文字サイズや色などを変更できます。

方法

Bloggerの、テーマ→カスタマイズ→HTMLを編集
を選択してください。
windowsなら「Ctrl + F」、Macなら「command+F」を押すと、左上に、「search: 」とでるので、そこに「/* Header」を入れて「Enter」を押します。
そうすると、下記のスクリプトがあると思います。

/* Header
----------------------------------------------- */
.header-outer {
  background: $(header.background.color) $(header.background.gradient) repeat-x scroll 0 -400px;
  _background-image: none;
}

.Header h1 {
  font: $(header.font);
  color: $(header.text.color);
  text-shadow: $(header.shadow.offset.left) $(header.shadow.offset.top) $(header.shadow.spread) rgba(0, 0, 0, .2);
}

.Header h1 a {
  color: $(header.text.color);
}

.Header .description {
  font-size: $(description.text.size);
  color: $(description.text.color);
}

.header-inner .Header .titlewrapper {
  padding: 22px $(header.padding);
}

.header-inner .Header .descriptionwrapper {
  padding: 0 $(header.padding);
}

このスクリプトの中身を書き変えれば、好みに合わせてヘッダーをカスタマイズできます。

ブログタイトルのカスタマイズ

.Header h1 {}の中身を変えれば、変更できます。
例えば本ブログでは、
.Header h1 {
  color: $(header.text.color);
  text-shadow: $(header.shadow.offset.left) $(header.shadow.offset.top) $(header.shadow.spread) rgba(0, 0, 0, .2);
  font-size: 1.2rem; /*ブログタイトルのサイズを変更*/
 font-family: Georgia, serif; /*ブログタイトルのフォントの種類を変更*/
}
と変更しています。

ブログ説明のカスタマイズ

.Header .description {}の中身を変えれば変更できます。
例えば、本ブログでは、
.Header .description {
  font-size: 0.7rem; /*ブログの説明の文字のサイズを変更*/
  color: $(description.text.color);
}
と変更しています。

以上で、ヘッダーのカスタマイズは一応終了です。
よりカスタマイズしたい方は、CSSの知識が必要になるので、
ヘッダーはサイトの顔になる部分なので、自分の納得できるものにしたいものですね。

Blogger 目次自動生成

この記事では、Bloggerで目次を自動生成する方法を、紹介します。
jQueryを使用しない方法を用いるため、サイトが重くならず軽快に表示されます。

特徴

  • 見出しタグ(h2,h3,h4…)を自動的に検出して、目次を自動生成
  • 目次の表示/非表示のリンクボタン付き
  • 階層的にhタグ(h2,h3,h4)を組んで目次化が可能
  • 段落番号を自動付与
  • Google検索結果のスニペットに目次リンクを表示

プラグインの導入

Bloggerの、テーマ→カスタマイズ→HTMLを編集
を選択してください。
windowsなら「Ctrl + F」、Macなら「command+F」を押すと、左上に、「search: 」とでるので、そこに「</head>」を入れて「Enter」を押します。
</head>の直前に下記ソースを挿入します。

<!-- [START] 目次作成プラグイン-->
<b:if cond='data:blog.pageType == "item"'>
  <script>
    //以下のオプションを好みに合わせて変更
    var toc_options = {
      target: ["h2", "h3", "h4"],
      autoNumber:  true,
      condTargetCount: 2,
      insertPosition: "top",
      showToc: true,
      width: "auto",
      marginTop: "20px",
      marginBottom: "20px",
      indent: "20px",
      postBodySelector: ".widget.Blog"
    };

    //これ以降のソースは編集しないでください
    ;(function (window) { var id_seq= 0; document.addEventListener(&apos;DOMContentLoaded&apos;, function () { var rootElement= document.querySelector(toc_options.postBodySelector); if (rootElement== null || typeof rootElement=== &quot;undefined&quot;) { return;} if (toc_options.target.length== 0) return; rootContent= searchHeadLine(toc_options, rootElement); if (rootContent.children.length &gt;= toc_options.condTargetCount) { var wrap= createElement(rootContent); appendElement(wrap);}}); function searchHeadLine(toc_options, rootElement) { var count= toc_options.target.length; var fn= function (index, element, parentContent) { var currentTarget= toc_options.target[index]; var nextTarget= index &lt; count - 1 ? toc_options.target[index + 1] : &quot;&quot;; var id= &quot;toc_headline_&quot; + (++id_seq); var content= createItem(currentTarget, text(element), index + 1, id); parentContent.children.push(content); element.id= id; var el= next(element); if (nextTarget== &quot;&quot;) { return;} var prevTarget= &quot;&quot;; for(var i= index; i &gt;= 0; i--) { prevTarget += (toc_options.target[i] + &quot;,&quot;);} while (true) { if (el== null || typeof el=== &quot;undefined&quot;) break; if (tagName(el)== currentTarget) break; if (tagName(el)== nextTarget) { fn(index + 1, el, content);} else { var nextElements= el.querySelectorAll(prevTarget + nextTarget); var breakFlg= false; for (var i= 0; i &lt; nextElements.length; i++) { if (tagName(nextElements[i]) != nextTarget) { exitFlg= true; break;} fn(index + 1, nextElements[i], content);} if (breakFlg) break;} var el= next(el);}}; var rootContent= createItem(&quot;ROOT&quot;, &quot;&quot;, 0); var elements= rootElement.getElementsByTagName(toc_options.target[0]); for (var i= 0; i &lt; elements.length; i++) { fn(0, elements[i], rootContent, &quot;&quot;);} return rootContent;} function createElement(rootContent) { var wrap= document.createElement(&quot;div&quot;); wrap.classList.add(&quot;b-toc-container&quot;); wrap.style.marginTop= toc_options.marginTop; wrap.style.marginBottom= toc_options.marginTop; if (toc_options.width== &quot;100%&quot;) { wrap.style.display= &quot;block&quot;;} else { wrap.style.width= toc_options.width;} var p= document.createElement(&quot;p&quot;); var span1= document.createElement(&quot;span&quot;); var span2= document.createElement(&quot;span&quot;); var span3= document.createElement(&quot;span&quot;); span2.classList.add(&quot;b-toc-show-wrap&quot;); span3.classList.add(&quot;b-toc-show-wrap&quot;); var a= document.createElement(&quot;a&quot;); span1.innerText= &quot;目次&quot;; span2.innerText= &quot;[&quot;; span3.innerText= &quot;]&quot;; a.href= &quot;javascript:void(0);&quot;; p.appendChild(span1); p.appendChild(span2); p.appendChild(a); p.appendChild(span3); var toggleToc= function (state) { var s= typeof state=== &quot;boolean&quot; ? state : hasClass(wrap, &quot;hide&quot;); if (s) { a.innerText= &quot;非表示&quot;; wrap.classList.remove(&quot;hide&quot;);} else { a.innerText= &quot;表示&quot;; wrap.classList.add(&quot;hide&quot;);}}; a.addEventListener(&apos;click&apos;, toggleToc); toggleToc(toc_options.showToc); var ul= document.createElement(&quot;ul&quot;); ul.classList.add(&quot;toc-root-list&quot;); rootContent.children.forEach(function (content, index) { createContentItemElement(ul, content, (index + 1) + &quot;&quot;);}); wrap.appendChild(p); wrap.appendChild(ul); return wrap;} function createContentItemElement(ul, content, no) { var li= document.createElement(&quot;li&quot;); li.classList.add(&quot;toc-list-item&quot;); var a= document.createElement(&quot;a&quot;); li.style.paddingLeft= toc_options.indent; ul.style.paddingLeft= 0; a.href= &quot;#&quot; + content.id; smoothScroll(a); if (toc_options.autoNumber) { var spanNm= document.createElement(&quot;span&quot;); spanNm.classList.add(&quot;toc-number&quot;); spanNm.innerText= no + &quot;.&quot;;} var spanText= document.createElement(&quot;span&quot;); spanText.classList.add(&quot;toc-text&quot;); spanText.innerText= content.text; if (toc_options.autoNumber) a.appendChild(spanNm); a.appendChild(spanText); li.appendChild(a); ul.appendChild(li); if (content.children.length &gt; 0) { var childUl= document.createElement(&quot;ul&quot;); childUl.classList.add(&quot;toc-sub-list&quot;); li.appendChild(childUl); content.children.forEach(function (childContent, index) { createContentItemElement(childUl, childContent, no + &quot;.&quot; + (index + 1));});}} function smoothScroll(a) { a.addEventListener(&apos;click&apos;, (e)=&gt; { e.preventDefault(); let href= a.getAttribute(&apos;href&apos;); let targetElement= document.getElementById(href.replace(&apos;#&apos;, &apos;&apos;)); const rect= targetElement.getBoundingClientRect().top; const offset= window.pageYOffset; const target= rect + offset - 0; window.scrollTo({ top: target, behavior: &apos;smooth&apos;, });});} function appendElement(element) { var el= null; var rootElement= document.querySelector(toc_options.postBodySelector); if (toc_options.insertPosition== &quot;firstHeadBefore&quot; || toc_options.insertPosition== &quot;firstHeadAfter&quot;) { el= rootElement.querySelector(toc_options.target[0]);} else if (toc_options.insertPosition== &quot;top&quot;) { el= rootElement;} if (el== null) return; if (toc_options.insertPosition== &quot;firstHeadBefore&quot;) { before(el, element);} else if (toc_options.insertPosition== &quot;firstHeadAfter&quot;) { after(el, element);} else if (toc_options.insertPosition== &quot;top&quot;) { before(el, element);}} function createItem(tagName, text, nestLevel, id) { return { tagName: tagName, text: text, children: [], nestLevel: nestLevel, id: id
};} function text(element) { return element.innerText;} function next(element) { return element.nextElementSibling;} function prev(element) { return element.previousElementSibling;} function tagName(element) { return element.tagName.toLowerCase();} function hasClass(element, className) { return element.classList.contains(className);} function parentElement(element) { return element.parentNode;} function after(element, insertElement) { var parent= parentElement(element); var nextEl= next(element); if (parent != null &amp;&amp; nextEl != null) { parent.insertBefore(insertElement, nextEl);}} function before(element, insertElement) { var parent= parentElement(element); if (parent != null) { parent.insertBefore(insertElement, element);}} })(window); 
  </script>
  <style type="text/css">
     .b-toc-container{background:#f9f9f9;border:1px solid #aaa;padding:10px;margin-bottom:1em;width:auto;display:table;font-size:95%}.b-toc-container p{text-align:center;margin:0;padding:0}.b-toc-container ul{list-style-type:none;list-style:none;margin:0;padding:0}.b-toc-container>ul{margin:15px 0 0}.b-toc-container.hide>ul{display:none}.b-toc-container ul li{margin:0;padding:0 0 0 20px;list-style:none}.b-toc-container ul li:after,.b-toc-container ul li:before{background:0;border-radius:0;content:""}.b-toc-container ul li a{text-decoration:none;color:#008db7!important;font-weight:400;display:flex;align-items:flex-start;flex-wrap:nowrap}.b-toc-container ul li .toc-number{margin:0 .5em 0 0;white-space:nowrap}.b-toc-container ul li .toc-text:hover{text-decoration:underline}
  </style>
</b:if>
<!-- [END] 目次作成プラグイン-->

コードを貼り付けたら、画面上の [テーマを保存] をクリックして、内容を保存します。
以上で、プラグインの導入は完了です。

オプションの設定方法

コピーしたコードに、toc_options = {…} と書かれている部分があります。
この JSON を直接編集して、目次の表示オプションを変更できます。

target

目次の作成。見出しタグを h1〜h6の 範囲で指定。
この機能とhタグの関係は以下になります。

  • 主見出し:h1タグ
  • 見出し :h2タグ
  • 小見出し:h3タグ
  • 準見出し:h4タグ

投稿記事内の章ごとの題名となる文字列にhタグを設定し、あらかじめ文章を階層化することが必要です。難しいことをいってそうですが、目次にしたい文字列を選択して、下記の写真の丸で囲ったところを変更するだけで、Bloggerの目次を自動生成してくれます。


autoNumber

目次に、自動的に連番を付けるか指定。

  • true  1 → 1.1 → 1.1.1の順に、階層化された連番を自動的に付与。
  • false  連番を非表示。

condTargetCount

目次を表示する見出の数を指定。
例えば、2 を指定した場合、targetオプションで指定したトップレベルの見出し(デフォルトではh2タグ)が、2つ以上あるとき、目次が表示されます。

insertPosition

目次の表示位置を以下から指定。

  • top 記事の最上部
  • firstHeadBefore 最初の見出しの前
  • firstHeadAfter 最初の見出しの後

showToc

初期の目次表示状態を指定。

  • true  初期状態で目次を表示。
  • false 初期状態で目次を非表示。

他の表示オプションも変更できますが、本記事は参考文献にある下記のサイトを参照して
つくらせていただいため、詳しくはそちらを参照してください。

参考文献

・スケ郎のお話. [Blogger] 目次を簡単に自動生成(忙しい人向けのコピペ素材). <https://www.sukerou.com/2018/10/blogger-table-of-contents-javascript.html> (参照日2022年8月21日).

git addで発生するエラー fatal: pathspec 'ファイル名' did not match any filesの解決策

GitHubに登録しようと、ターミナル等のコマンドラインで"git add ファイル名"を実行すると、

fatal: pathspec 'ファイル名' did not match any files

上記の様なエラーが発生したことはありませんか。本記事ではこのエラーへの対応策を解説します。

エラー要因

エラーを訳してみると、「'ファイル名' はどのファイルにも一致していませんでした。」
ということをいっています。

つまり、'ファイル名'が見つけられずエラーが発生したということです。
Gitに登録していないファイルがあると出るエラーです。

解決策

ファイルが見つからないといっているので、
まずは、ファイル名に誤字脱字が無いかを確認しましょう。

ファイル名が合っていたら、"git add"を'ファイル名'を保管しているディレクトリ上で実行されているかを確認しましょう。gitは階層構造を基本としていて、ディレクトリ毎に管理されています。下の階層にファイルがあったとしても、作業しているディレクトリにファイルが無ければgitは読み込んでくれません。そのため、指定のディレクトリ(cd ディレクトリ名)へ移動して、"git add"を打ち込みましょう。

また、下記のようにファイル名にスペースがあると、
コマンドだと認識されてしまうためエラーとなります。

git add file program
出力
fatal: pathspec 'file' did not match any files

パスを""で囲むとスペースがあってもエラーを吐きません。

git add "file program"

ただし、予期せぬエラーが起きうるため、
ファイル名にスペースを入れないほうがよいでしょう。

Kaggle House Prices

Kaggleの回帰問題のチュートリアルである、住宅価格の予測「House Prices: Advanced Regression Techniques」に挑戦しました。

このチュートリアルの目的は、与えられた79の説明変数(敷地面積や天井の高さ等の住宅データ)をもとにSalePrice(住宅の販売価格)を予測することです。

ライブラリの準備

まず今回使用するライブラリをインポートします。

# numpy , pandas
import numpy as np 
import pandas as pd

# 可視化用ライブラリ
import matplotlib.pyplot as plt
# Jupyter Notebookの中でインライン表示する場合の設定(これが無いと別ウィンドウでグラフが開く)
%matplotlib inline
import seaborn as sns

# 歪度
from scipy.stats import skew

# scikit-learn
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import Ridge
from sklearn.linear_model import LassoCV

# xgboost
import xgboost as xgb


学習データの読み込み

訓練データとテストデータをPandasのデータフレームに読み込みます。


# 訓練データをデータフレームに読み込む
train = pd.read_csv(
    "../input/house-prices-advanced-regression-techniques/train.csv")
# テストデータをデータフレームに読み込む
test = pd.read_csv(
    "../input/house-prices-advanced-regression-techniques/test.csv")
print('train shape:', train.shape) # 訓練データの形状を出力
print('test shape:', test.shape)   # テストデータの形状を出力

出力結果
train shape: (1460, 81)
test shape: (1459, 80)

訓練データには1460レコード、テストデータには1459レコードのデータが収録されています。訓練データのカラム数は81ですが、テストデータでは予測に使用する'SalePrice'がないので、カラム数は80となっています。訓練データの情報をDataFrame.info()メソッドで出力してみます。

# 訓練データの情報を出力
train.info()

出力結果
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1460 entries, 0 to 1459
Data columns (total 81 columns):
 #   Column         Non-Null Count  Dtype 
---  ------         --------------  ----- 
 0   Id             1460 non-null   int64 
 1   MSSubClass     1460 non-null   int64 
 2   MSZoning       1460 non-null   object 
 3   LotFrontage    1201 non-null   float64
 4   LotArea        1460 non-null   int64 
 5   Street         1460 non-null   object 
 6   Alley          91 non-null     object 
 7   LotShape       1460 non-null   object 
 8   LandContour    1460 non-null   object 
 9   Utilities      1460 non-null   object
 10  LotConfig      1460 non-null   object
 11  LandSlope      1460 non-null   object 
 12  Neighborhood   1460 non-null   object 
 13  Condition1     1460 non-null   object 
 14  Condition2     1460 non-null   object 
 15  BldgType       1460 non-null   object 
 16  HouseStyle     1460 non-null   object 
 17  OverallQual    1460 non-null   int64 
 18  OverallCond    1460 non-null   int64 
 19  YearBuilt      1460 non-null   int64 
 20  YearRemodAdd   1460 non-null   int64 
 21  RoofStyle      1460 non-null   object 
 22  RoofMatl       1460 non-null   object 
 23  Exterior1st    1460 non-null   object 
 24  Exterior2nd    1460 non-null   object 
 25  MasVnrType     1452 non-null   object 
 26  MasVnrArea     1452 non-null   float64
 27  ExterQual      1460 non-null   object
 28  ExterCond      1460 non-null   object
 29  Foundation     1460 non-null   object 
 30  BsmtQual       1423 non-null   object 
 31  BsmtCond       1423 non-null   object 
 32  BsmtExposure   1422 non-null   object 
 33  BsmtFinType1   1423 non-null   object 
 34  BsmtFinSF1     1460 non-null   int64 
 35  BsmtFinType2   1422 non-null   object 
 36  BsmtFinSF2     1460 non-null   int64 
 37  BsmtUnfSF      1460 non-null   int64 
 38  TotalBsmtSF    1460 non-null   int64 
 39  Heating        1460 non-null   object 
 40  HeatingQC      1460 non-null   object 
 41  CentralAir     1460 non-null   object 
 42  Electrical     1459 non-null   object 
 43  1stFlrSF       1460 non-null   int64 
 44  2ndFlrSF       1460 non-null   int64 
 45  LowQualFinSF   1460 non-null   int64 
 46  GrLivArea      1460 non-null   int64 
 47  BsmtFullBath   1460 non-null   int64 
 48  BsmtHalfBath   1460 non-null   int64 
 49  FullBath       1460 non-null   int64 
 50  HalfBath       1460 non-null   int64 
 51  BedroomAbvGr   1460 non-null   int64 
 52  KitchenAbvGr   1460 non-null   int64 
 53  KitchenQual    1460 non-null   object 
 54  TotRmsAbvGrd   1460 non-null   int64 
 55  Functional     1460 non-null   object 
 56  Fireplaces     1460 non-null   int64 
 57  FireplaceQu    770 non-null    object 
 58  GarageType     1379 non-null   object 
 59  GarageYrBlt    1379 non-null   float64
 60  GarageFinish   1379 non-null   object 
 61  GarageCars     1460 non-null   int64 
 62  GarageArea     1460 non-null   int64 
 63  GarageQual     1379 non-null   object 
 64  GarageCond     1379 non-null   object
 65  PavedDrive     1460 non-null   object 
 66  WoodDeckSF     1460 non-null   int64 
 67  OpenPorchSF    1460 non-null   int64 
 68  EnclosedPorch  1460 non-null   int64 
 69  3SsnPorch      1460 non-null   int64 
 70  ScreenPorch    1460 non-null   int64 
 71  PoolArea       1460 non-null   int64 
 72  PoolQC         7 non-null      object 
 73  Fence          281 non-null    object 
 74  MiscFeature    54 non-null     object 
 75  MiscVal        1460 non-null   int64 
 76  MoSold         1460 non-null   int64 
 77  YrSold         1460 non-null   int64 
 78  SaleType       1460 non-null   object 
 79  SaleCondition  1460 non-null   object 
 80  SalePrice      1460 non-null   int64 
dtypes: float64(3), int64(35), object(43)
memory usage: 924.0+ KB


対数変換

訓練データの'SalePrice'には、住宅の販売価格が格納されています。'SalePrice'の分布状況を確認してみます。 

sns.displot(train['SalePrice'])

 

出力結果


分布状況が低価格帯に偏っていて、やや右側に尾を引くような分布になっています。



回帰分析をする上では正規分布にならなければ精度が下がるので、対数変換を利用して正規分布に変換します。



# SalePriceについて底をxの対数に変換し、
# 元の値と共にデータフレームに登録
prices = pd.DataFrame({'price':train['SalePrice'],
                       'log(price)':np.log(train['SalePrice'])})
print(prices, '\n')
# 'price'の対数変換前後の歪度を出力
print('price skew       :', skew(prices['price']))
print('log(price) skew:', skew(prices['log(price)']))

# "SalePrice"の変換前と変換後をヒストグラムにする
plt.rcParams['figure.figsize'] = (12.0, 6.0)
prices.hist()



出力結果

# SalePriceの値を、底をeとする対数に変換する
train["SalePrice"] = np.log(train["SalePrice"])
ヒストグラムを見ると、対数変換したことで正規分布に近づいているのが分かります。また、歪度(分布が正規分布からどれだけ歪んでいるかを表す統計量)をみても、対数変換後の方が0に近い値であるため、分布が正規分布に近づいたということがいえます。今回のように対数変換することで正規分布に近づく変数は多くあります。今回は、RMSEで評価するために対数変換したら、たまたま正規分布に近づきました。ただし、目的変数を正規分布にする必要は必ずしもあるわけではありません。回帰分析において、残差が正規分布であることが望ましいのですが、目的変数を正規分布に近づけると残差も正規分布に近づく場合が多くあります。

訓練データとテストデータを連結

ここで、データの前処理をまとめて、行えるように、訓練データとテストデータを連結して1つのデータフレームにします。前処理では、'Id'と'SalePrice'のカラムは必要ないので、'MSSubClass'~'SaleCondition'のカラムのみを抽出してから連結します。
# 訓練データとテストデータからMSSubClass~SaleConditionのカラムのみを抽出して連結
all_data = pd.concat((train.loc[:,'MSSubClass':'SaleCondition'],
                      test.loc[:,'MSSubClass':'SaleCondition']))
# 連結したデータを出力
all_data

出力結果









欠損値処理

ここからは、欠損値の処理をしていきます。欠損値の処理には、主に以下の2つがあります。
①欠損値を含む行または列の削除
②欠損値を代表値などで埋める

最も欠損が多かったのはPoolQC(プールの質)で、更に文字列のデータした。このカラムは削除したくなりますが、Kaggleの提供しているデータdata_description.txtにPoolQCを確認すると次のように記載されています。

PoolQC: Pool quality
Ex Excellent
Gd Good
TA Average/Typical
Fa Fair
NA No Pool

つまり、NaN(欠損)は「No Pool」の事であり、データの欠損そのものが情報となっています。他の変数を確認してもobject型の変数のほとんどが該当の施設が存在しないという意味でデータが欠損していました。そこで、今回は、object型の欠損値はNAで埋めることにします。(より詳細にデータを確認すると、他のカラムを参照すると、施設が存在しているけれど、データが欠損している箇所もあるので、もう少し丁寧に前処理を行うべきだと思います。)

#na_col_listに欠損値を含む説明変数のリストを作成
na_col_list = all_data.isnull().sum()[all_data.isnull().sum()>0].index.tolist()
#連結したデータのobject抽出
na_obj = all_data[na_col_list].dtypes[all_data[na_col_list].dtypes=='object'].index.tolist() 
#データの欠損値をNAで補間
for i in na_obj:
    all_data.loc[all_data[i].isnull(),i] = 'NA' 


ここで、もう一度データの欠損状況を確認します。

# データの欠損状況
all_data.isnull().sum()[all_data.isnull().sum()>0].sort_values(ascending=False)  


出力結果


最も欠損の多いLotFrontageから見ていきます。LotFrontageの説明をみると、 Linear feet of street connected to property(物件に隣接した道路の長さ)とかいてあります。区画サイズによってLotFrontageの値がほぼ決まると考えられます。他の方の方法を参考にさせていただいたのですが、区画サイズは地区ごとに標準のサイズが決まっているので、地区ごとにLotFrontageの値がほぼ決まるのではないかと考えられます。そこで、地区を表すNeighborhoodごとのLotFrontageを箱ひげ図で見てみます。


sns.boxplot(data=all_data, x='Neighborhood', y='LotFrontage')

出力結果


確かに見た感じ、地区ごとにLotFrontageの値がほぼ決まってそうです。そこで、LotFrontageの欠損値をNeighborhoodごとの代表値で埋めることにします。代表値といえば、平均値が思い浮かびますが、今回は外れ値の影響が大きそうなので、中央値で埋めることにしました。そのためのコードが合っているのか自信がないのですが、もし間違っていたらご指摘いただきたいです。

all_data.LotFrontage[all_data.LotFrontage.isnull()] = all_data.groupby("Neighborhood").LotFrontage.median()[all_data.loc[all_data.LotFrontage.isnull(),"Neighborhood"]]

次に欠損値の多かったGarageYrBltをみていきます。データを具体的に見てみます。

all_data['GarageYrBlt']

出力結果


GarageYrBltは見ての通り、ガレージが建設された年を表すデータです。そのため、欠損値は、ガレージがないことを意味しているのではないかと推測できます。しかし、ガレージが無いことを表すデータはGarageYrBltの他にGarageQual、GarageCondから読み取れます。また、GarageYrBltはYearBuilt(住宅の建設された年)とも相関が高そうであり、欠損値を埋めることが難しいので、この列は削除することにします。

all_data = all_data.drop('GarageYrBlt', axis=1)

残りの欠損値のある列について考えましょう。ほとんどの列が、該当の施設が存在しないという意味でデータが欠損していました。そのため、欠損値は、0で埋めていきます。

all_data = all_data.fillna(0)

数字の大小関係が予測に影響しない列の処理

数字が入っているが、数字の大小関係が予測に影響を与えない方が良いものは文字列にすべきです。

例えば、MSSubClass(住居の種類)は
{'1-STORY 1946 & NEWER ALL STYLES' : 20 ,'1-STORY 1945 & OLDER' : 30 , '1-STORY W/FINISHED ATTIC ALL AGES' : 40,・・・, '2 FAMILY CONVERSION - ALL STYLES AND AGES' : 190 }と割り当てられている場合、数の大小に意味はないので{'1-STORY 1946 & NEWER ALL STYLES' : '20' ,'1-STORY 1945 & OLDER' : '30' , '1-STORY W/FINISHED ATTIC ALL AGES' : '40' ,・・・, '2 FAMILY CONVERSION - ALL STYLES AND AGES' : '190' }に変換します。

num_str_list = ['MSSubClass','YrSold','MoSold']
for i in num_str_list:
    all_data[i] = all_data[i].astype(str)  


相関の確認

#相関係数が0.7以上の変数の組を表示
all_data_corr = all_data.corr()
all_data_corr[all_data_corr > 0.7].stack()


出力結果

LotFrontage    LotFrontage      1.000000

LotArea        LotArea          1.000000

OverallQual    OverallQual      1.000000

OverallCond    OverallCond      1.000000

YearBuilt      YearBuilt        1.000000

YearRemodAdd   YearRemodAdd     1.000000

MasVnrArea     MasVnrArea       1.000000

BsmtFinSF1     BsmtFinSF1       1.000000

BsmtFinSF2     BsmtFinSF2       1.000000

BsmtUnfSF      BsmtUnfSF        1.000000

TotalBsmtSF    TotalBsmtSF      1.000000

                  1stFlrSF         0.801376

1stFlrSF       TotalBsmtSF      0.801376

                 1stFlrSF         1.000000

2ndFlrSF       2ndFlrSF         1.000000

LowQualFinSF   LowQualFinSF     1.000000

GrLivArea      GrLivArea        1.000000

               TotRmsAbvGrd     0.808354

BsmtFullBath   BsmtFullBath     1.000000

BsmtHalfBath   BsmtHalfBath     1.000000

FullBath       FullBath         1.000000

HalfBath       HalfBath         1.000000

BedroomAbvGr   BedroomAbvGr     1.000000

KitchenAbvGr   KitchenAbvGr     1.000000

TotRmsAbvGrd   GrLivArea        0.808354

               TotRmsAbvGrd     1.000000

Fireplaces     Fireplaces       1.000000

GarageCars     GarageCars       1.000000

               GarageArea       0.889890

GarageArea     GarageCars       0.889890

               GarageArea       1.000000

WoodDeckSF     WoodDeckSF       1.000000

OpenPorchSF    OpenPorchSF      1.000000

EnclosedPorch  EnclosedPorch    1.000000

3SsnPorch      3SsnPorch        1.000000

ScreenPorch    ScreenPorch      1.000000

PoolArea       PoolArea         1.000000

MiscVal        MiscVal          1.000000

dtype: float64

相関係数を確認してみたところ、
・TotalBsmtSF    1stFlrSF
・GrLivArea      TotRmsAbvGrd
・GarageArea     GarageCars    

以上の列の組に強い相関がありました。GarageCars(車容量)とGarageArea(車庫の大きさ)などは項目の意味からも相関の強さは明らかです。これらの変数は多重共線性の点から一つに絞る必要があります。多重共線性とは学習モデルに用いる説明変数同士で相関係数が高い場合、数値的に不安定な予測を引き起こしてしまうことを指します。そのため、相関関係が高いと考えられる説明変数を外したり、それらの変数を合成して新しい変数をつくる等の処理が必要です。

特徴量エンジニアリング

特徴量エンジニアリングでは予測の精度向上に貢献しそうな新しい変数を追加していきます。

# 特徴量エンジニアリングによりカラムを追加する関数
def add_new_columns(df):
    # 建物内の総面積 = 1階の面積 + 2階の面積 + 地下の面積
    df['TotalSF'] = df['1stFlrSF'] + df['2ndFlrSF'] + df['TotalBsmtSF']

    # 一部屋あたりの平均面積 = 建物の総面積 / 部屋数
    df['AreaPerRoom'] = df['TotalSF']/df['TotRmsAbvGrd']

    # 築年数 + 最新リフォーム年 : この値が大きいほど値段が高くなりそう
    df['YearBuiltPlusRemod']=df['YearBuilt']+df['YearRemodAdd']

    # お風呂の総面積
    # Full bath : 浴槽、シャワー、洗面台、便器全てが備わったバスルーム
    # Half bath : 洗面台、便器が備わった部屋)(シャワールームがある場合もある)
    # シャワーがない場合を想定してHalf Bathには0.5の係数をつける
    df['TotalBathrooms'] = (df['FullBath'] + (0.5 * df['HalfBath']) + df['BsmtFullBath'] + (0.5 * df['BsmtHalfBath']))
  
  # 合計の屋根付きの玄関の総面積 
    # Porch : 屋根付きの玄関 日本風にいうと縁側
    df['TotalPorchSF'] = (df['OpenPorchSF'] + df['3SsnPorch'] + df['EnclosedPorch'] + df['ScreenPorch'] + df['WoodDeckSF'])

    # 2階の有無
    df['Has2ndFloor'] = df['2ndFlrSF'].apply(lambda x: 1 if x > 0 else 0)

# カラムを追加
add_new_columns(all_data)

このように、設備があるかないかを0,1で分類したり、面積等を合計したりして、新たな特徴量を作ります。工夫次第で様々な特徴量を作成できそうです。

特徴量選択

先ほどは、新たに特徴量を作成しましたが、特徴量は多ければ良いというものではありません。ノイズにより予測精度が落ちてしまうことがあるのです。

そこで、相関が高いカラムを片方削除したり、重要度が低いカラムを削除したりする等してノイズによる影響を減らします。

dr_cols = ['GarageCars','1stFlrSF','2ndFlrSF','TotalBsmtSF','TotalSF','TotRmsAbvGrd','YearBuilt','YearRemodAdd','FullBath','HalfBath','BsmtFullBath','BsmtHalfBath','OpenPorchSF','3SsnPorch','EnclosedPorch','ScreenPorch','WoodDeckSF' ]
for i in dr_cols:
    all_data = all_data.drop(i, axis=1)

カテゴリ変数のエンコーディング

カテゴリ変数とは、身長や年齢のように数値で表せる変数ではなく、グループ・属性(色・国など)を分類する用途で示される変数を指します。また、カテゴリ変数は「名義変数」と「順序変数」で分けることができます。名義変数は、並び替えや順序付けができないカテゴリ変数です。それに対して、順序変数は、並び替えや順序付けが可能なカテゴリ変数です。カテゴリ変数はそのまま学習に使うことができないので、数値に変換することが求められます。その処理過程をエンコーディングといいます。 

Label Encoding


まず、順序変数に対してはLabel Encodingを行います。
Label Encodingとは、カテゴリ変数の項目数に応じて連番を渡すデータ変換方法です。後に紹介するOne-Hot-Encodingと異なり、新しく生成される変数は1つのみでいいという特徴があります。scikit-learnのLabelEncoderを用いると、それぞれのカテゴリに自動で数値を割り当てることができます。なお、この時の数値はABC順で自動的に決められます。モデルによってはカテゴリの順番も重要な要素になってくるので、そのような場合は自分で順番を指定する必要があるため、可能ならば、1つずつ順序変数を詳細に見ていくべきだと思います。

また、カテゴリ変数を数値変数に変換しても、その数値変数には「数値的な意味」がありません。例えば、「a~e」のカテゴリ変数を、「0~4」の数値変数に変換したとしても
「e:4」が「b:1」の4倍大きいわけではありません。

そのため、機械学習において「決定木」をベースにした手法以外では、正確ではないモデルが出来上がる可能性があります。一方で「決定木」や「ランダムフォレスト」といった手法では、数値変数も分岐を繰り返して予測値に反映できるため、学習に用いることが可能です。コンペなどでよく利用されているGBDTにおいても、Label Encodingはよく利用されています。

例えば、PoolQCは以下のように変換します。
PoolQC: Pool quality
Ex Excellent     →4
Gd Good                →3
TA Average/Typical →2
Fa Fair                   →1
NA No Pool             →0



#ラベルエンコーディングするクラス
from sklearn.preprocessing import LabelEncoder

#インスタンス
LE = LabelEncoder()

LE_cols = ['Utilities','ExterQual','ExterCond','BsmtQual','BsmtCond','BsmtExposure','BsmtFinType1','BsmtFinType2','HeatingQC','KitchenQual','FireplaceQu','GarageFinish','GarageQual','GarageCond','PavedDrive','PoolQC','Fence']
for i in LE_cols:
    #Label エンコーディング
    LE.fit_transform(all_data[i].values)

    #データ変換
    all_data[i] = LE.fit_transform(all_data[i].values)  

One-Hot-Encoding


名義変数に対しては、One-Hot-Encodingを行います。One-Hot-Encodingでは、カテゴリ変数に含まれる項目を新たな列として扱い、各列の値に0または1を与えていく方法です。カテゴリに用意された対象項目に該当する場合「1」該当しない場合「0」を渡します。
大量のカテゴリが含まれているカテゴリ変数の場合は、ダミー変数の数が膨大になってしまうのでこの方法を用いるのは難しい場合もあります。また、他にも多重共線性に注意する必要があります。

多重共線性を回避するためには「One-Hot-Encodingで生成した列を1つ削除する」だけで良いです。get_dummyies()メソッドを使用する場合、drop_first=Trueとして引数を渡すと、最初の列を削除できます。(ちなみに、この手法はdummy encodingと呼ばれています。多重共線性を回避するための方法としては、他にもeffecct encodingという手法もあります。)

例えば、SaleConditionには、Normal、Abnorml、AdjLand等の6つの対象項目がありますが、drop_first=Trueとしてget_dummyies()メソッドを使用すると、SaleCondition_Abnormlの列が削除されて以下のように変換されます。つまり、他の5つの対象項目全てが0と観測された場合、SaleConditionはAbnormlと判断できるため、ダミー変数を1つ削除したところでSaleConditionの情報が消失することはありません。

変換前                 変換後




# One Hotエンコーディングで生成した列を1つ削除する
all_data = pd.get_dummies(all_data,drop_first=True)  
今回は上記2つのEncodingを使いましたが、Count Encoding、Target Encodingといった手法もあり、より工夫できそうなところです。ここで、もう一度相関係数を確認して、相関関係が高いと考えられる説明変数を外します。
#相関係数が0.7以上の変数の組を表示
all_data_corr = all_data.corr()
all_data_corr[(all_data_corr > 0.7) & (all_data_corr != 1)].stack()
出力結果
KitchenAbvGr           MSSubClass_90            0.751203
                       BldgType_Duplex          0.751203
Has2ndFloor            HouseStyle_2Story        0.753648
MSSubClass_190         BldgType_2fmCon          0.975118
MSSubClass_20          HouseStyle_1Story        0.756927
MSSubClass_45          HouseStyle_1.5Unf        0.864323
MSSubClass_60          HouseStyle_2Story        0.753204
MSSubClass_80          HouseStyle_SLvl          0.958428
MSSubClass_85          HouseStyle_SFoyer        0.755819
MSSubClass_90          KitchenAbvGr             0.751203
MSZoning_FV            Neighborhood_Somerst     0.867135
Neighborhood_NPkVill   Exterior2nd_Brk Cmn      0.798647
Neighborhood_Somerst   MSZoning_FV              0.867135
BldgType_2fmCon        MSSubClass_190           0.975118
BldgType_Duplex        KitchenAbvGr             0.751203
HouseStyle_1.5Unf      MSSubClass_45            0.864323
HouseStyle_1Story      MSSubClass_20            0.756927
HouseStyle_2Story      Has2ndFloor              0.753648
                       MSSubClass_60            0.753204
HouseStyle_SFoyer      MSSubClass_85            0.755819
HouseStyle_SLvl        MSSubClass_80            0.958428
Exterior1st_AsphShn    Exterior2nd_AsphShn      0.706864
Exterior1st_CemntBd    Exterior2nd_CmentBd      0.983411
Exterior1st_HdBoard    Exterior2nd_HdBoard      0.887999
Exterior1st_MetalSd    Exterior2nd_MetalSd      0.969712
Exterior1st_Plywood    Exterior2nd_Plywood      0.740022
Exterior1st_Stucco     Exterior2nd_Stucco       0.729910
Exterior1st_VinylSd    Exterior2nd_VinylSd      0.978178
Exterior1st_Wd Sdng    Exterior2nd_Wd Sdng      0.861610
Exterior2nd_AsphShn    Exterior1st_AsphShn      0.706864
Exterior2nd_Brk Cmn    Neighborhood_NPkVill     0.798647
Exterior2nd_CmentBd    Exterior1st_CemntBd      0.983411
Exterior2nd_HdBoard    Exterior1st_HdBoard      0.887999
Exterior2nd_MetalSd    Exterior1st_MetalSd      0.969712
Exterior2nd_Plywood    Exterior1st_Plywood      0.740022
Exterior2nd_Stucco     Exterior1st_Stucco       0.729910
Exterior2nd_VinylSd    Exterior1st_VinylSd      0.978178
Exterior2nd_Wd Sdng    Exterior1st_Wd Sdng      0.861610
SaleType_New           SaleCondition_Partial    0.986573
SaleCondition_Partial  SaleType_New             0.986573
dtype: float64

出力結果をみると、MSSubClassとHouseStyle、Exterior1stとExterior2ndはもともと関係性が強そうなので、エンコーディングする前にどちらかのカラムを削除した方が良かったかもしれません。
dr_cols = ['KitchenAbvGr','BldgType_Duplex','BldgType_2fmCon','HouseStyle_1Story','HouseStyle_SLvl','HouseStyle_SFoyer','MSZoning_FV','Exterior2nd_Brk Cmn','Exterior2nd_AsphShn','Exterior2nd_CmentBd','Exterior2nd_HdBoard','Exterior2nd_MetalSd','Exterior2nd_Plywood','Exterior2nd_Stucco','Exterior2nd_VinylSd','Exterior2nd_Wd Sdng','SaleCondition_Partial' ]
for i in dr_cols:
    all_data = all_data.drop(i, axis=1)

回帰モデルによる学習


標準化

例えば、面積と年数では同じ単位で比較できないので、どちらかの変数を重視してしまうモデルができてしまう可能性があります。そこで、特徴量がもつ値の重みを平等にするために、データを標準化します。標準化は、平均0、分散1にスケーリングして扱いやすいものに整えることであり、データの分布が正規分布に従っている場合に特に効果的な手法です。そのため、SalePriceと同じように、歪度が大きい説明変数の分布を対数変換して正規分布に近づけてから標準化してもいいのですが、標準化は必ずしもデータの分布が正規分布でなくても使えるので、本記事ではそのまま標準化することにします。
from sklearn.preprocessing import StandardScaler

#訓練データとテストデータを標準化
sc = StandardScaler()
all_data = sc.fit_transform(all_data)

データの切り分け

結合した訓練データとテストデータをそれぞれ切り分け、対数変換したSalePriceを、訓練時の正解データとして用意します。
# 訓練データとテストデータに分ける
X_train = all_data[:train.shape[0]]
X_test = all_data[train.shape[0]:]
y = train.SalePrice

線形回帰


線形回帰では、回帰式を用いた、予測が行われます。線形回帰の目的は、回帰式と実測値(正解値)が最小になるwを求めることになります。このときの誤差E(w)の最も単純な方法はMSE(平均二乗誤差)です。

今回の予測では、MSEの平方根をとったRMSE(二乗平方根誤差)を誤差関数(損失関数)として用いています。RMSEは、予測値が上振れ(正解値を大きく上回る)した場合に、より大きな損失E(w)になるので、価格の予想に適しているといえます。

rmse_cv()関数をRMSEを計算する関数として定義します。検証は、クロスバリュエーションでデータを5個に分割して行うことにします。
def rmse_cv(model):
    """二乗平均平方根誤差
    
    Parameters:
      model(obj): Modelオブジェクト
    Returns:
      (float)訓練データの出力値と正解値とのRMSE
    """
    # クロスバリデーションによる二乗平均平方根誤差の取得
    rmse = np.sqrt(
        -cross_val_score(
            model, X_train, y,
            scoring="neg_mean_squared_error", # 平均二乗誤差を指定
            cv = 5))                          # データを5分割
    return(rmse)

機械学習は、一般的に学習データに対する損失関数を最小化するように学習しますが、
これだけでは細かくフィッティングした複雑なモデルが生成され、過学習が起こります。

そこで、損失関数にモデルの複雑さを表す指標(正則化項)を加え、これを最小化するよう学習すれば、
性能と複雑さ、すなわち過学習と未学習のバランスを取った学習が実現できます。

MSEを最小にするようなパラメータを求める方法が最小二乗法ですが、複雑さを制御する(過剰適合しないようにする)パラメータがあれがいいのですが、それはありません。パラメータを導入する方法としてリッジ回帰とラッソ回帰があります。

リッジ回帰

リッジ回帰は正則化された線形回帰の1つで、線形関数の誤差関数に重みの二乗和(L2正則化項)を加えたものです。L2正則化項は、数学的にものの長さを表すユークリッド距離とも呼ばれています。パラメータの値が増大するのを防ぐことでオーバーフィッティングを防ぐ。
リッジ回帰は、sckit-learnのsklearn.linear_model.Ridgeクラスで実装し、L2正則化の強度(係数)を10パターン用意して、それぞれの強度で精度を測定します。

正則化の強弱、つまり、モデルの複雑さは、alphaの値を変化させることにより決められます。

alphaを増やす -> 正則化が強くなる -> モデルは簡潔になる
alphaを減らす -> 正則化が弱くなる -> モデルは複雑になる

alphaの値が何が良いかはデータ次第です。ちなみに、alphaを0とすると、通常の最小二乗法による回帰になります。
# リッジ回帰モデルを生成
model_ridge = Ridge()

# L2正則化の強度を10パターン用意
alphas = [0.05, 0.1, 1, 10, 50, 100, 200, 300, 400, 500]
# 正則化の各強度でリッジ回帰を実行
# 5分割のクロスバリデーションでRMSEを求め、その平均を取得
cv_ridge = [rmse_cv(Ridge(alpha = alpha)).mean() 
            for alpha in alphas]

# cv_ridgeをSeriesオブジェクトに変換
cv_ridge = pd.Series(cv_ridge, index = alphas)
# スコアを出力
print('Ridge RMSE loss:')
print(cv_ridge, '\n')
# スコアの平均を出力
print('Ridge RMSE loss Mean:')
print(cv_ridge.mean())
  
# 正則化の強度別のスコアをグラフにする
plt.figure(figsize=(10, 5)) # 描画エリアのサイズ
plt.plot(cv_ridge)        # cv_ridgeをプロット
plt.grid()                # グリッド表示
plt.title('Validation - by regularization strength')
plt.xlabel('Alpha')
plt.ylabel('RMSE')
plt.show()

出力結果






ラッソ回帰

ラッソ回帰は正則化された線形回帰の1つで、線形関数の誤差関数に重みの和(L1正則化項)を加えたものです。L1正則化項は、各要素の絶対値の和をとったもので、マンハッタン距離とも言われます。「碁盤の目状の町である2点間を移動するのに歩く距離」みたいなイメージです。ラッソ回帰では自動的に重要な特徴量を選択する役割を果たしていますが、その理由はL1正則化を用いるといくつかの重みパラメータが0になり、重みパラメータの値が0になることで、特徴量の値も0になっているため不要な特徴量を削ることができます。ラッソ回帰は多くの説明変数の係数が0となる(スパース性を持つ)ため、リッジ回帰と比べてより強い過学習防止効果が得られます。ラッソ回帰は、説明変数の数 > 学習データ数では有効に機能しませんが、今回はこの条件は余裕で満たしています。

Lassoにも複雑さの度合いを制御するパラメータalphaがあります。alphaのデフォルトは1.0で、リッジ回帰と同じように小さくするほど複雑なモデルになります。

ラッソ回帰モデルは、sckit-learnのsklearn.linear_model.Lassoクラスで実装できますが、本記事では複数の正則化の強度を実行できるLassoCVクラスを使ってみます。
from sklearn.linear_model import LassoCV

# ラッソ回帰モデルで推定する
# L1正則化項を4パターンで試す
model_lasso = LassoCV(
    alphas = [1, 0.1, 0.001, 0.0005]).fit(X_train, y)

print('Lasso regression RMSE loss:')                # クロスバリデーションによる
print(rmse_cv(model_lasso))                         # RMSEを出力

print('Average loss:', rmse_cv(model_lasso).mean()) # RMSEの平均を出力
print('Minimum loss:', rmse_cv(model_lasso).min())  # RMSEの最小値を出力
print('Best alpha  :', model_lasso.alpha_)          # 採用されたalpha値を出力

出力結果

ラッソ回帰とリッジ回帰の使い分けのですが、過学習を防ぐために、使用する特徴量が減っても良い場合はラッソ回帰、過学習を防ぎたいが、使用する特徴量が全て重要な場合はリッジ回帰を選択すると良いです。

またRidgeとLassoのペナルティを組合せたものとしてElasticNetがあります。ElasticNetは多くの線形手法(線形回帰、Ridge、Lasso)を内包しているため、線形手法の中でも汎用性が高く、有力な選択肢となりますが、チューニングすべきパラメータが増えるという欠点もあるため、本記事では扱いません。

GBDT(勾配ブースティング木)


GBDTとは、学習アルゴリズムにあまり高度なものを使わず、その代わりに予測値の誤差を、新しく作成したアルゴリズムが次々に引き継ぎながら、誤差を小さくしていく手法のことです。
機械学習では、ランダムフォレストがよくアルゴリズムとして用いられます。ランダムフォレストとは、例えば、3つの決定木(学習器)があれば、それを一斉に実行して、多数決で予測値を決めます。これに対してGBDTは、決定木が増えるに従って、その決定木が持つ誤差を小さくしていく考え方です。

GBDTの実装

GBDTは、XGBoost、LightGBM、CatBoostなどのライブラリで提供されているので、それぞれインポートして使えます。本記事では、XGBoostを使いますが、Learning API版とscikit-LearnのAPI版の2種類があるので、検証とモデル作成用においてそれぞれ使うことにします。

Learning API

まずは、決定木の数を多めにして損失の推移を確かめたいので、Learning APIのxgboost.cv()を使ってみます。このメソッドは、XGboost本体であるxgboostオブジェクトを使って、クロスバリュエーションを用いた検証を行い、履歴を戻り値として返します。

決定木の深さを5、学習率を0.1にして学習してみます。決定木の数は1000にしておいて、アーリーストッピングにより制御します。
# Learning APIのXGBoostモデルで使用される、データオブジェクトDMatrixを生成する
dtrain = xgb.DMatrix(X_train, label = y)
#dtest = xgb.DMatrix(X_test)

# 決定木の深さ5、(3~9の間で1刻みで設定。5から試してみるといい。)
#学習率0.1 
params = {"max_depth":5, "eta":0.1}
# xgboostモデルでクロスバリデーションを実行
cross_val = xgb.cv(
    params,
    dtrain, # トレーニングされるデータ
    num_boost_round=1000,     # 決定木の本数
    early_stopping_rounds=50) # アーリーストッピングの監視回数(一般的に50程度が良いとされている)
cross_val

出力結果
# 30回以降の検証データと訓練データのRMSEをグラフにする
plt.figure(figsize=(8, 6)) # 描画エリアのサイズ
plt.plot(cross_val.loc[30:,["test-rmse-mean", "train-rmse-mean"]])
plt.grid()               # グリッド表示
plt.xlabel('num_boost_round')
plt.ylabel('RMSE')
plt.show()

出力結果

scikit-Learn API

アーリーストッピングが効いて、決定木が410のところで停止しました。scikit-LearnのXGBoost実装であるXGBModel()で、決定木の数を242、決定木の深さを5、学習率を0.1に設定してモデルを作成し、fit()で学習させてみます。
# xgboostで学習する
model_xgb = xgb.XGBRegressor(
    n_estimators=242,  # 決定木の本数
    max_depth=5,       # 決定木の深さ
    learning_rate=0.1) # 学習率0.1
model_xgb.fit(X_train, y)

print('xgboost RMSE loss:')
print(rmse_cv(model_xgb).mean())

出力結果

アンサンブル

これまでに、リッジ回帰モデル、ラッソ回帰モデル、GBDTそれぞれで、学習してきました。この結果を受け、損失の低かったラッソ回帰とGBDTを採用することにして、それぞれでテストデータを用いた予測を行うことにします。

ラッソ回帰もGBDTどちらも、predict()メソッドで予測できます。ただし、SalePriceは対数変換してから学習しているので、予測する際には対数変換前の値に戻さなければいけません。これには、NumPyのexp()メソッドを使うことにします。
lasso_preds = np.exp(model_lasso.predict(X_test))
xgb_preds = np.exp(model_xgb.predict(X_test))

予測が完了したらそれぞれの結果をアンサンブルして最終結果を決定します。損失の低さに応じて重み付き平均でアンサンブルすることにし、ラッソ回帰を0.7、GBDTを0.3にしてアンサンブルします。
preds = lasso_preds*0.7 + xgb_preds*0.3

提出用csvを作成


SalePriceの予想データをCSVファイルにまとめます。
solution = pd.DataFrame({"id":test.Id, "SalePrice":preds})
solution.to_csv("ridge_sol.csv", index = False)

あとは出力されたcsvファイルを提出するだけです。

スコアは0.12882で順位は4400人中955位でした。

予測結果としてはまだまだで、前処理で外れ値の除去をする等まだまだ精度を向上できそうですが、回帰分析の基本的な流れを学ぶことができました。

参考文献


Blogger 数式 表示 MathJax

Bloggerで数式を表示する方法を解説します。
MathJax を使うと簡単にブログ中に TeX と同様の形式で数式をきれいに埋め込めます。

Bloggerの、
テーマ→カスタマイズ→HTMLを編集
を選択してください。

そして、<head>と</head>の間(例えば</head>の前)に
以下のコードを追加してください。
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js" type="text/javascript">    
    MathJax.Hub.Config({
        HTML: ["input/TeX","output/HTML-CSS"],
        TeX: { extensions: ["AMSmath.js","AMSsymbols.js"], 
               equationNumbers: { autoNumber: "AMS" } },
        extensions: ["tex2jax.js"],
        jax: ["input/TeX","output/HTML-CSS"],
        tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ],
                   displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
                   processEscapes: true },
        "HTML-CSS": { availableFonts: ["TeX"],
                      linebreaks: { automatic: true } }
    });
</script>
</head>を数千行あるコードの中から探すのは大変に思えますが、
「Ctrl + F」を押すと、簡単に探し出せます。
左上に、「search: 」とでるので、そこに「</head>」を入れて
「Enter」を押せば見つかります。

ここまでで、準備は終わりました。

使い方

まず、HTMLビューにします。そして、TeX形式で数式を書けばいいです。
例えば、数式のみを1行に表示させたい時は下記のように$$ $$で囲んで書くと、

$$ \frac{d}{d x} x^2 = 2x \tag{1} \\ $$

こんな風に表示されます。(独立行表示)


$$ \frac{d}{d x} x^2 = 2x \tag{1} \\ $$


数式だけでなく、文章も一緒に書きたい時は下記のように$ $で囲んで書くと、該当部分が数式化されて、
定積分$ \int_{a}^{b}f(x)dx $の値を求めることを,関数f(x)をaからbまで積分するという


こんな風に表示されます。(インライン表示)

定積分$ \int_{a}^{b}f(x)dx $の値を求めることを,関数f(x)をaからbまで積分するという


数式からTeX形式への変換

これで、数式をTeX形式で埋め込めるようになりましたが、私をはじめTeXを書ける人はそこまで多くないと思います。そこで数式からTeX形式への変換を行うために役立つと感じたサイトをいくつか紹介させていただきます。

MyScript  手書きした数式を読み取ってTeX形式に変換してくれます。
HostMath 数式のパーツを選択して数式を作成し、TeX形式に変換してくれます。

上記のサイトを使えば、TeXを書けない人でも変換可能ですが、TeXを自分で書けるようにするために勉強されたい方は以下のサイト等を参照してください。

参考文献


Blogger ソースコード表示 Google-code-prettify

Bloggerにソースコードを埋め込む方法として、

Google-code-prettify

を使う方法があります。

Bloggerの、
テーマ→カスタマイズ→HTMLを編集
を選択してください。

そして、<head>と</head>の間(例えば</head>の前)に
以下のコードを追加してください。

<script src='https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?skin=sons-of-obsidian' type='text/javascript'/>

</head>を数千行あるコードの中から探すのは大変に思えますが、
「Ctrl + F」を押すと、簡単に探し出せます。
左上に、「search: 」とでるので、そこに「</head>」を入れて
「Enter」を押せば見つかります。

ここまでで、準備は終わりました。

使い方

まず、HTMLビューにします。そこにコードを書きます。
そして、コードを<pre class="prettyprint"></pre>ではさみます。

<pre class="prettyprint”>
ここにコードを書く
</pre>

メジャーな言語は、下記のようにclassに追加指定することで、
何の言語のコードなのか自動で判別してくれます。

<pre class="prettyprint lang=python”>
pythonのコードを書く
</pre>

行番号を表示するときは、以下のようにlinenumsを付加します。

<pre class="prettyprint lang=python linenums">

横スクロールバーを付けるには、以下のように
style="overflow:auto; overflow-y:hidden;"を指定します。

<pre class="prettyprint linenums" style="overflow:auto; overflow-y:hidden;">

実践例

例えば以下のようにHTMLビューに打ち込むと、

<pre class="prettyprint lang=python linenums" style="overflow:auto; overflow-y:hidden;">
def test_hello_world():
    str = "Hello World"
    print(str)
test_hello_world()
</pre>
こんな風に表示されます。
def test_hello_world():
    str = "Hello World"
    print(str)
test_hello_world()

HTMLのタグがある場合

「<」や「>」は次のようなHTML特殊文字と呼ばれるものに変換してやる必要があります。
毎回タグを特殊文字に変換するのは大変ですから、以下のサイトのツールを使用して変換してもらいましょう。

参考文献








人気の投稿