2020年1月24日金曜日

KaggleのTitanicタイタニックの正解率をあげる。

おじさん、やっと暇ができました。
途中までやっていた、正解率78%のKaggleのタイタニックの正解率をとりあえず80%くらいにあげようと思います。

前回まではKaggleをC言語でやるための準備だったので、今回が本当のスタートです。

まず、SVMが線形カーネルだとよくないので、RBFカーネルをサポートしているlibsvmに変えます。まぁRBFカーネルのほうが真ん中取り出せるし。

次に、親兄弟の数と子供の数は足して一つのパラメーターのほうがよさそうなので足します。これだけで1%くらい正解率があがり79%に。

あと港と生存率が関係するので、港の値も入力。
SとCとQとパラメータなしにどの値を割り振るのが良いか16通り試験します。
Sは1、Cは0、Qは-1にマッピングするのがパラメーター的には一番よさそう
これで0.5%あがり。79.49%に。

客室もAかBかCだと船の上の方なので生存率が高そう。
このABだけなのかCまでなのかとかを決めるのが大変ですね。
どうやらABCにいるかいないかで違うっぽい。
これも0.5%あがり。79.9%に。

なんでこう微妙に足りないのかな。
もうチケット名以外の入力は全部使いました。

ここまでは簡単に上がるんですが、ここからがきつい。
とりあえず、SVMの予想がはずれているしている人だけをピックアップしてみます。

------------------------
1179,NG,1.000000,-1.000000,-0.400,1,2,Snyder, Mr. John Pillsbury,21228,B45
1182,NG,1.000000,-1.000000,-0.258,1,1,Rheims, Mr. George Alexander Lucien,PC 17607,
1192,NG,1.000000,-1.000000,-0.200,3,1,Olsson, Mr. Oscar Wilhelm,347079,
1201,NG,1.000000,-1.000000,0.125,3,2,Hansen, Mrs. Claus Peter (Jennie L Howard),350026,
1203,NG,1.000000,-1.000000,-0.450,3,1,Vartanian, Mr. David,2658,
1205,NG,-1.000000,1.000000,-0.075,3,1,Carr, Miss. Jeannie,368364,
1207,NG,-1.000000,1.000000,-0.575,3,1,Hagardon, Miss. Kate,AQ/3. 30631,
1213,NG,1.000000,-1.000000,-0.375,3,1,Krekorian, Mr. Neshan,2654,F E57
1233,NG,1.000000,-1.000000,-0.200,3,1,Lundstrom, Mr. Thure Edvin,350403,
1246,NG,1.000000,-1.000000,-1.000,3,4,Dean, Miss. Elizabeth Gladys Millvina"""",C.A. 2315,
1250,NG,1.000000,-1.000000,-0.258,3,1,O'Keefe, Mr. Patrick,368402,
1261,NG,1.000000,-1.000000,-0.275,2,1,Pallas y Castello, Mr. Emilio,SC/PARIS 2147,
------------------------

どうも、傾向として3等室で港とか年齢とか客室とか欠損データが多い人が予想をはずしているのね。それ以外はほぼランダム。
この3等室で生きている人を当てるのが難しい。

なので3等室でかつSVMが予想を外している人のリストを眺めます。
SVMが予想を外している人の特徴を特徴ベクトルとして入れる作戦。
おじさんにデータ解析ツールなんていらない。
おじさんはC言語とコンソールだけでいいもん。
おじさん、リストの文字と数値を眺めるだけで分析してやる。

リストを眺めると3等室でチケットの名前がPPで始まっている人と番号が1から始まている人が生存率が高そうなのでその人にフラグを立てます。

80.382775%

やったー、これで目標の80%越え達成です。
そうかぁ全項目使わないとだめなのね。
こんな感じで勝手な予想をたててKaggleは正解率を上げていくようです。

もうちょっと頑張ればもっと上がりそうだけど、とりあえず本日はここまで。
これをタイタニック以外の問題でやれば銅メダルがもらえるのね。
おじさんとりあえずは銅メダルはもらえそうなことはわかった。


------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static char *strtokrev(char *s1, int s2) {
static char *str = 0;
int f = 0;

if (s1)str = s1;
else s1 = str;

if (!s1)return NULL;

while (1) {
if (!*str) {
str = 0;
return(s1);
}
if (*str == '\r' || *str == '\n') {
*str = 0;
str = 0;
return(s1);
}
if (*str == '\"' && f == 0) {
str++;
s1 = str;
f = 1;
continue;
}
if (*str == '\"' && f == 1 && *(str + 1) == '\"') {
str += 2;
continue;
}
if (*str == '\"' && f == 1) {
*str++ = 0;
f = 0;
continue;
}
if (*str == s2 && f == 0) {
*str++ = 0;
return(s1);
}
str++;
}
}


struct person{
int id;
int survive;
int cls;
char name[64];
int sex;
int age;
double s_age;
int sib;
int per;
int f_mr;
int f_mas;
char ticket[64];
char cabin[64];
double fare;
int emb;
int f_oth1;
int f_oth2;
int f_oth3;
};

struct person pli[1024];
struct person pli2[1024];

char* firstnum(const char* a)
{
if (a == NULL)return a;
while (*a) {
if (*a < '0') { a++; continue; }
if (*a > '9') { a++; continue; }
if (*(a+1) < '0') { a++; continue; }
if (*(a+1) > '9') { a++; continue; }
break;
}
return a;
}
void store(struct person* plist,int pos, int n, const char* str)
{
char buf[256];
char*p;
switch (n)
{
case 0:
sscanf(str, "%d", &(plist[pos].id));
break;
case 1:
sscanf(str, "%d", &(plist[pos].survive));
if (plist[pos].survive == 0)plist[pos].survive = -1;
break;
case 2:
sscanf(str, "%d", &(plist[pos].cls));
break;
case 3:
plist[pos].f_mr = 0;
if (strlen(str) < 64 - 1)strcpy(plist[pos].name, str);
if (strstr(plist[pos].name, "Mr."))plist[pos].f_mr = 1;
break;
case 4:
buf[0] = 0;
plist[pos].sex = -1;
if (strlen(str) < 64 - 1)strcpy(buf, str);
if(strstr(buf,"female"))plist[pos].sex = 1;
else if (strstr(buf, "male"))plist[pos].sex = -1;
break;
case 5:
plist[pos].age = -1;
sscanf(str, "%d", &(plist[pos].age));
break;
case 6:
sscanf(str, "%d", &(plist[pos].sib));
break;
case 7:
sscanf(str, "%d", &(plist[pos].per));
break;
case 8:
if (strlen(str) < 64 - 1)strcpy(plist[pos].ticket, str);
if (plist[pos].cls == 3) {
p = firstnum(plist[pos].ticket);
if (*p == '1') {
plist[pos].f_oth1 = 1;
}
if (plist[pos].ticket[0]=='P') {
plist[pos].f_oth1 = 1;
}
}

break;
case 9:
sscanf(str, "%lf", &(plist[pos].fare));
break;
case 10:
if (strlen(str) < 64 - 1)strcpy(plist[pos].cabin, str);
plist[pos].f_mas = 0;
if (strchr(str, 'A'))plist[pos].f_mas = 1;
if (strchr(str, 'B'))plist[pos].f_mas = 1;
if (strchr(str, 'C'))plist[pos].f_mas = 1;
break;
case 11:
plist[pos].emb = 1;
if (strchr(str, 'S'))plist[pos].emb = 1;
if (strchr(str, 'C'))plist[pos].emb = 0;
if (strchr(str, 'Q'))plist[pos].emb = -1;
break;
default:
break;
}
}
int read_plist(struct person* plist,const char* fn,int ff)
{
FILE *fp=NULL;
char buf[256];
int i, f, id, re, ct = 0;
char *p,*q;

int ct2=0;
static double ave,dmax = 0, dmin = 1000,sub=0;

fp = fopen(fn,"rb");
if (fp == NULL)goto end;
fgets(buf, sizeof(buf), fp);
while (1) {
buf[0] = 0;
fgets(buf, sizeof(buf), fp);
if (buf[0] == 0)break;
for (i = 0; i < 14; i++) {
p = buf;
if (i != 0)p = NULL;
if (ff && i == 1)continue;
q = strtokrev(p, ',');
if (q == NULL)q = "";
store(plist,ct,i, q);
}
ct++;
}
if (ff == 0) {
for (i = 0; i < ct; i++) {
if (plist[i].age == -1)continue;
ct2++;
sub += plist[i].age;
if (plist[i].age > dmax)dmax = plist[i].age;
if (plist[i].age < dmin)dmin = plist[i].age;
}
ave = sub; ave /= ct2;
}
//printf("ave=%f\n",ave);
for (i = 0; i < ct; i++) {
if (plist[i].age == -1) {
plist[i].s_age = ave;
}
else plist[i].s_age = plist[i].age;

plist[i].s_age = (plist[i].s_age - dmin)*2/dmax-1;
}

end:
if(fp)fclose(fp);
return 0;
}

void print_plist(struct person* plist,FILE* fd)
{
int i;
if (fd == NULL)return;
for (i = 0; i < 1024; i++) {
if (plist[i].id == 0)break;
fprintf(fd, "%d 1:%d 2:%d 3:%d 4:%f 5:%d 6:%d 7:%f 8:%d 9:%d\n",
plist[i].survive, plist[i].cls, plist[i].f_mr, plist[i].sex, plist[i].s_age,
plist[i].sib + plist[i].per + 1, plist[i].f_mas, plist[i].fare, plist[i].emb,plist[i].f_oth1);
}
}

void comp(const char* fn1,const char* fn2)
{
FILE *f1 = NULL, *f2 = NULL;
char buf1[256];
char buf2[256];
int ct = 0;
int ok = 0;
double d1, d2;
int id, f;
double per;
f1 = fopen(fn1, "rt");
f2 = fopen(fn2, "rt");
if (f1 == NULL || f2 == NULL)goto err;

fgets(buf1, sizeof(buf1), f1);
while (1) {
buf1[0] = 0;
buf2[0] = 0;
d1 = 0;
d2 = 0;
f = 0;
fgets(buf1, sizeof(buf1), f1);
fgets(buf2, sizeof(buf2), f2);
if (buf1[0] == 0 || buf2[0] == 0)break;
sscanf(buf1, "%d,%lf", &id, &d1);
sscanf(buf2, "%lf", &d2);
if (d1 == 0)d1 = -1;

if (d1 > 0 && d2 > 0)f = 1;
if (d1 < 0 && d2 < 0)f = 1;
if (f)ok++;
if (f == 0) {
printf("%d,%s,%1lf,%1lf,%0.3lf,%d,%d,%s,%s,%s\n", 892 + ct, f ? "OK" : "NG",d1, d2,
pli2[ct].s_age,pli2[ct].cls, pli2[ct].sib+pli2[ct].per+1,
pli2[ct].name,pli2[ct].ticket, pli2[ct].cabin);
}
ct++;
}
per = ok * 100;
per /= ct;
printf("ct=%d  ok=%d  %f%%\n", ct, ok, per);
err:
if (f1)fclose(f1);
if (f2)fclose(f2);
}



int main()
{
FILE* fp;
read_plist(pli,"train.csv",0);
read_plist(pli2,"test.csv",1);
fp = fopen("yomei_train.csv","wb");
print_plist(pli,fp);
if (fp)fclose(fp);
fp = fopen("yomei_test.csv", "wb");
print_plist(pli2, fp);
if (fp)fclose(fp);
//printf("----------pli\n");
//print_plist(pli, stdout);
//printf("----------pli2\n");
//print_plist(pli2, stdout);
system("svm-scale -s yomei_scale.txt yomei_train.csv  > yomei_train_scale.csv");
system("svm-scale -r yomei_scale.txt yomei_test.csv > yomei_test_scale.csv");
system("svm-train yomei_train_scale.csv yomei_model.txt");
system("svm-predict yomei_test_scale.csv yomei_model.txt yomei_predict.csv");

comp("gender_submission2.csv", "yomei_predict.csv");
return 0;
}
------------------------



2020年1月20日月曜日

ReactをJavaScriptのみで使う

おじさん、最近のフロントエンドには全くついていけません。
そのなかでも特にわからいのがReact。

Reactって開発者向けのライブラリで学習に時間がかかるらしい。
おじさん、開発者だけどわからないぞ。
JSXとかBabel.jsってなんなんだよ!

しかもReactはJSXを使わないでJavaScriptのみで書くととってもめんどくさい。
Vue.jsやAnglarjsみたいにJSファイルだけで使えるとよいんだけどなあ。
どうもおじさんとReactは相性が悪いらしい。

どうにかJavaScriptのみでReact使えないかなぁ。
三日間悩み続けたら、関数一つ追加すればJavaScriptのみでReactが使えるんじゃね?ということに気づきました。

というわけで、ReactをJSXを使わないでJavaScriptのみで簡単に動作するように、関数を一つ追加してみました。

ReactYo.render('#root2',data);

配列にいろいろ情報セットして、ReactYo.renderを呼ぶだけ。
これでReactをVue.jsやAngularjs風に使える!
{{}}も動作するようにしてみました。
あとforとかも実装すれば、タグの複製とかもできそう。



------------------------------
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Reactサンプル</title>
</head>
<body>
  <h1>Reactサンプル</h1>

  <div id="root"><div id="child1">hoge</div><div id="child2">hoge</div><div>{{getMsg()}}</div></div>
  <div id="root2">{{getMsg()}}</div>
  <div id="root3"><input id="input1" /></div>

  <script src="react.development.js"></script>
  <script src="react-dom.development.js"></script>
  <script src="babel.js"></script>
  <script language="Javascript">


//
// ReactYo
//

ReactYo={};
ReactYo.prop={};
ReactYo.idcount=0;
ReactYo.render=function(id,data){
  function getId(){
    var ret="_id_"+ReactYo.idcount;
    ReactYo.idcount++;
    return ret;
  }
  function escapeString(str){
if(str==null)return str;
var ev,ret,a=str.indexOf("{{"),b=str.indexOf("}}");
if(a==-1 || b==-1)return str;
ret=str.substring(0,a);
ev=str.substring(a+2,b);
ret+=eval(ev);
ev=str.substring(b+2);
ret+=ev;
return ret;
  }
  function createElement(elem,data,kid){
    if(elem==null)return "";
    if("children" in elem ==false || elem.children.length==0){
      var str=elem.innerHTML;
      if(kid in ReactYo.prop)str=ReactYo.prop[kid];
      else ReactYo.prop[kid]=str;
      return escapeString(str);
    }
    var i,len=elem.children.length;
    var li=[];
    li.push(React.Fragment);
    li.push({});
    for(i=0;i<len;i++){
      var pro={},id=null;
      if("id" in elem.children[i] && elem.children[i].id!="")id=elem.children[i].id;
      if(id!=null && data && id in data){
  pro=data[id];
          //println(JSON.stringify(pro));
      }
      if(id==null)id=getId();
      if(id)pro["id"]=id;
      var el=createElement(elem.children[i],data,id);
      if(el!=null && el!=""){
          li.push(React.createElement(elem.children[i].tagName.toLowerCase(),pro,el));
       }
      else{
          li.push(React.createElement(elem.children[i].tagName.toLowerCase(),pro));
      }
    }
    return React.createElement.apply(null,li);
  }
  if(id==null)return;
  var kid=null;
  var elem=document.querySelector(id);
  if("id" in elem)kid=elem.id;
  if(kid==null)kid=getId();
  var reactelem=createElement(elem,data,kid);
  ReactDOM.render(reactelem,elem);
}


//
// data
//


data={
  child2:{
    onClick:function(){onClick("sss")},
  },
  input1:{
    value:"134",
    onChange:function(e){onChange(e.target.value)},
    onBlur:function(e){onBlur(e.target.value)},
    className:"cls1",
  },
};


//
// functions
//

println=function(str)
{
alert(str);
}

onClick=function()
{
println("onClick()");
}

onChange=function(s)
{
  data.input1.value=s;
  ReactYo.render('#root3',data);
}

onBlur=function(s)
{
var str="onBlur(\"";
str+=s;
str+="\")";
//println(str);
console.log(str);
}

count=0;
getMsg=function(s)
{
var ret="message"+count;
count++;
return ret;
}

onTimeout=function()
{
  //println("onTimeout()");

  data.input1.className="cls2";
  data.input1.value="777";
  ReactYo.render('#root2',data);
  ReactYo.render('#root3',data);
  ReactYo.render('#root',data);
}



//
// render
//


println("React 16.12.0");

ReactYo.render('#root2',data);
ReactYo.render('#root',data);
ReactYo.render('#root3',data);

setTimeout(onTimeout,5000);
------------------------------


2020年1月16日木曜日

Vue.jsで遊んでみた。

おじさん、いまさら、React.js、Babel.js、Vue.jsを使ってみました。


最近、JavaScriptを拡張したり、HTML中でPHPのようにJavaScriptを実行したり、仮想DOMとかがはやっているのね。

最近のフロンドエンドにはついていけない・・・・・・

そんななか、Vue.jsのHTMLのタグやアトリビュートの中でPHPのようにJavaScriptを実行できる機能はなんとなく理解できてとても気に入りました。
Angular.jsだと変数とか関数にアクセスするの大変だもんね。
これはちょー便利。

また、Vue.jsはJavaScriptから一切DOMを操作しなくて良いように設計されているのもよいですね。
HTMLの中を汚さないので、デザイナーの納品データをすぐに更新できるのもよいです。

Vue.jsならばjQueryから乗り換えられそうな気がする。
おじさん、C言語風にVue.jsのコードを書いてみました。

------------------------------
<!DOCTYPE html>
<html lang="ja">
<body>
  <h1>Vueサンプル</h1>

<div id="app" v-bind:tag="getMessage()">
  {{ getMessage() }}</br>
  {{ hogeee}}
</div>
<div id="aaa">
<div v-for="n in kaisuu" v-bind:id="'id_'+n" v-on:click="onClickApp(n)">{{ n }}:{{ getHoge(n) }}</div>
</div>

<script src="vue.js"></script>
<script language="Javascript">

var kaisuu=5;
var hogeee="123456789";

println=function(str){
alert(str);
}
getMessage=function(){
var ret="hoge";
return ret;
}

onVueReady=function()
{
println("onVueReady()");
}

onClickApp=function(n)
{
var str="onClickApp(";
str+=n;
str+=")";
println(str);
}

getHoge=function(n)
{
var ret="hoge_";
ret+=n;
return ret;
}


new Vue({
  el: '#app',
  data: {
    hogeee: hogeee,
  },
  methods: {
    getMessage: getMessage,
  },
  created: onVueReady,
});

new Vue({
  el: '#aaa',
  data: {
    kaisuu: kaisuu,
  },
  methods: {
    getHoge: getHoge,
    onClickApp: onClickApp,
  },

});


</script>
</body>
</html>
------------------------------

2020年1月14日火曜日

Eigenで重回帰分析をやってみる。

おじさんのおうちのポストに「今あなたのお手持ちのマンションを売ると○○○○万円です。」というチラシがよく入っています。

どうも全ての部屋毎に違う値が入っているらしい。
これ、どうやって計算しているんだろね。
なんでおじさんちの値段がわかるの?

https://www.ydc.co.jp/column/mi/mieruka02.html

いろいろ調べると、重回帰分析というやつをやればマンションの価格を求められるらしい。
行列を最小二乗法でいろいろ計算するっぽい。
だけど答えを求めるには逆行列と転置行列の掛け算をするだけじゃないか。
これ、マンションの売買データだけあればだれでも作れるぞ。

ということで、本日はC/C++言語で重回帰分析をやってみます。
C/C++で行列の計算を行う場合は、Googleが作ったEigenというライブラリを使えば簡単。
前にも書きましたが、Eigenはヘッダーのみで構成されているライブラリなので、インストールとかしないで、ヘッダーをインクルードするだけで使えます。
便利だねぇ。

ネットでEigenで重回帰分析やっている人を検索しましたが一人しかいませんでした。
http://cpp-kai.doorblog.jp/archives/2340422.html
さみしいねぇ。ひとりってどういうことよ。
これだとこの人があっているかどうかもわからないじゃないか。

Xt = X.transpose();
beta = (Xt*X).inverse()*Xt*y;

重要なのはこの2行。
たったこれだけで重回帰分析ができるのですね。

これを参考にしながら、ここにある体重と身長、腹囲、胸囲のエクセルでの重回帰分析をC++でやってみました。
https://udemy.benesse.co.jp/ai/multiple-regression-analysis.html

-----------------
-0.152591
  1.13726
 0.148569
-----------------

まぁ行列のサイズ変えるだけなんで簡単。
計算してみるとなんかエクセルと計算が合わない。

調べてみると定数項を0にするかどうかで係数の計算結果が違うらしい。
定数項を0の場合、とりあえずエクセルと計算値が一致。できた。

でも定数がある場合ってどうやって計算するんだろ?
ここがネットでやっている人が一人しかいない場合つらい。
なにをやっても必ずどこかではまるデータ解析。

ちょー調べてみると、入力行列の一番左の列に全部1を入れるとよいらしい。
よく考えると計算上そうだよね。

おーこれで日本初?切片付きの重回帰分析をEigenでできた。
エクセルとも完全一致。

重回帰分析ってeigenを使うと数行でできるのね。
これを体重じゃなくてマンションのデータでやれば価格がわかるんですね。
これが出来ると履歴書に「マンションの査定をすることができる」とかかけるんでしょうか?

今日はC++言語で重回帰分析でした。
次は相関係数やろうかなぁ。

-----------------
#include "Eigen_Core"
#include "Eigen_LU"
#include <iostream>

using namespace std;

int main(int argc, char* argv[])
{
#if 0
//切片なし
Eigen::Matrix<double, 10, 3> X;
X <<
177.5, 88.2, 95.2,
173.0, 75.6, 83.3,
168.8, 70.2, 78.3,
165.5, 67.0, 75.6,
164.5, 62.4, 72.3,
160.2, 60.0, 69.9,
170.2, 69.4, 77.9,
180.2, 80.2, 88.2,
182.3, 88.3, 93.5,
160.3, 62.0, 74.9;
Eigen::Matrix<double, 10, 1> y;
y <<
82.3,
70.4,
65.3,
66.2,
54.1,
55.0,
58.8,
77.7,
92.8,
60.4;
Eigen::Matrix<double, 3, 10> Xt;
Xt = X.transpose();
Eigen::Matrix<double, 3, 1> beta;
#else
//切片あり
Eigen::Matrix<double, 10, 4> X;
X <<
1, 177.5, 88.2, 95.2,
1, 173.0, 75.6, 83.3,
1, 168.8, 70.2, 78.3,
1, 165.5, 67.0, 75.6,
1, 164.5, 62.4, 72.3,
1, 160.2, 60.0, 69.9,
1, 170.2, 69.4, 77.9,
1, 180.2, 80.2, 88.2,
1, 182.3, 88.3, 93.5,
1, 160.3, 62.0, 74.9;
Eigen::Matrix<double, 10, 1> y;
y <<
82.3,
70.4,
65.3,
66.2,
54.1,
55.0,
58.8,
77.7,
92.8,
60.4;
Eigen::Matrix<double, 4, 10> Xt;
Xt = X.transpose();
Eigen::Matrix<double, 4, 1> beta;
#endif
beta = (Xt*X).inverse()*Xt*y;
std::cout << beta << std::endl;

return 0;
}
-----------------



2020年1月10日金曜日

タイタニックの生き残り戦略を考える

おじさん、ブログのアップする順番を間違えてしまった。
前回のブログでtitanicの生存者を当てる機械学習の中の上くらいのモデルができました。
精度があまり良くないけれど、せっかく作ったモデルを有効活用しないといけないので、今回は以下のサイトのように、自分がもしタイタニック号に乗ったら生き残るかどうかを調べてみました。
おじさんの会社がタイタニック号だと思って真剣に調べます。

https://nehori.com/nikki/2019/12/13/post-13895/


このサイトと同じように2等室にのって、金20、自分の年齢をtest.csvにいれて計算してみます。
svmlightは1がほぼ生存、-1が即死です。
とりあえずまず「Mr.」がある場合とない場合を実験してみます。

----------------------------
1310,2,"Mr. Yomei",male,44,1,0,2668,20,,C
1311,2,"Yomei",male,44,1,0,2668,20,,C
----------------------------

----------------------------
-1.2885661
-0.212499
----------------------------

うおー、おじさん、ディカプリオのように死んでしまうじゃないか。
しかも「Mr.」がついていると即死。
「Mr.」のブランド力強すぎ。
おじさんまだ死にたくない!
なので「Mr.」なんていらない。
そう思っていろいろ調べると、どうも成人しているおじさんは強制的に「Mr.」がつくらしい。
「Mr.」とれないのかよー。

ならおじさん、お金を積んでみます。
とりあえず倍の運賃。やっぱ世の中金だよね。

----------------------------
1310,2,"Mr. Yomei",male,44,1,0,2668,40,,C
1311,2,"Yomei",male,44,1,0,2668,40,,C
----------------------------

----------------------------
-1.2511204
-0.1750533
----------------------------

倍の金をつんでも0.04しか動かない。
これ、生き残るためには相当な金を積まないといけないっぽい。
おじさんそんな金ないんだけど。


なら、一人で船に乗ってみました。

----------------------------
1310,2,"Mr. Yomei",male,44,0,0,2668,40,,C
1311,2,"Yomei",male,44,0,0,2668,40,,C
----------------------------

----------------------------
-1.0021617
0.073905436
----------------------------

すげー、一人で船に乗ると0.25アップ。
「Mr.」がついてないならばどうにか生存率50%以上になってきました。
これ、ネットでささやかれている結果と違うんだよなぁ。
たぶん、この辺データがまだ正しく入力されてないんだろなぁ。

だけど「Mr.」がついている状態だとどうやってもおじさんは死んでしまう。
やっぱ年齢がいけないのか。
二等室で生き残った中年のおじさんはいないの?
そもそも二等室って飛行機で言うとビジネスクラスじゃん。おじさんそんなの乗ったことないんだけど。三等室でシミュレーションしないといけないのでは?
二等室でこのような状況ならば三等室はもう絶望的。

ということで、おじさんは絶対にタイタニック号に乗ってはいけないことがわかりました。

このように機械学習を使えば、お金をかけないでどうすれば生き残るかがシミュレーションでわかるのです。
逆に言うとよいモデルとデータを構築できると、それで金を節約できるので、浮いたお金をもらえるようになるのです。
これはもっとディープラーニング勉強してもっと正確なモデルデータを構築せねば。

機械学習って一度モデルを構築すればこのようにいろいろシミュレーションができて面白いですね。
きっと企業の生き残り戦略とかも同じようにやるんだろなぁ。

KaggleのTitanicの完全な答えを作る。

おじさん、「レポートを書け」とか「宿題を出せ」とかが大嫌いなのです。
なのでTatanicの問題も出したくない!
だって同じ作業を何回もするのめんどくさいでしょ。
そもそもプログラムを始めた理由も中学生の時に連立方程式を解くのがめんどくさいからだったので。
いま思うと、この連立方程式のおかげで宿題が嫌いになり、プログラムができるようになりました。それよかったのかな?

そこで、今日は前回作ったtaitnicの問題の正解率を求めるプログラムを作ってみます。
ところで、Kaggleってどうやって正解率計算してるんだろね。
いろいろ調べると、どうやらtitanic3.csvというデータをネットからとってくると、タイタニックの乗客1310人全員のデータがあるらしい。
それをとってくれば100%正解のデータがつくれそうなので、正解率が求められそうです。
早速titatic3.csvのなかからtest.csvにいる人のデータを取ってきて答えを作ります。

手動でやるとめんどくさいので100%正解のデータを作るプログラムを作成。
答えを作成している途中にわかったんだけど、train.csvやtest.csvの名前のところのダブルコーテーションのエスケープ処理が間違っている。これ本当にtest.csv正しいのかな?
こういう変なところに時間を取られます。

出題者もデータの変換を間違うくらいなので、機械学習のプログラムって、データが間違っているのか、変換プログラムが間違っているのか、解析プログラムが間違っているのか、ほんとデバッグが大変。

いろいろあったけど、どうにか100%正解データ作成完了。
早速先日の自分の答えをチェックしてみます。

------------------------------------
.......
OK  1.000000  1.357631
OK  -1.000000  -1.140641
OK  -1.000000  -1.098839
NG  1.000000  -0.307569
ct=418  ok=326  77.990431%
------------------------------------

とりあえず僕の適当に作ったモデルの本当の正解率が求められました。
なるほど、お手本の解答から1.5%上がったのね。
先日アップしたコードや方法がとりあえず間違ってなさそうです。よかーったー。
でも成績が上位に入るためには正解率をあと2%あげないといけないのか。

とりあえずC言語でスクラッチで作ったSVMの計算プログラムやデータ変換プログラムが正しいことはわかった。
これでc言語だけで頑張ってデータ解析するスタートラインに立てました。
C言語でデータ解析ってどうやるんだろ。
続く。

------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static char *strtokrev(char *s1, int s2) {
static char *str = 0;
int f = 0;

if (s1)str = s1;
else s1 = str;

if (!s1)return NULL;

while (1) {
if (!*str) {
str = 0;
return(s1);
}
if (*str == '\r' || *str == '\n') {
*str = 0;
str = 0;
return(s1);
}
if (*str == '\"' && f == 0) {
str++;
s1 = str;
f = 1;
continue;
}
if (*str == '\"' && f == 1 && *(str + 1) == '\"') {
str += 2;
continue;
}
if (*str == '\"' && f == 1) {
*str++ = 0;
f = 0;
continue;
}
if (*str == s2 && f == 0) {
*str++ = 0;
return(s1);
}
str++;
}
}

char list[2000][20][80];
char list2[2000][20][80];

void read_list()
{
int i, j, c=0;
FILE *fp = NULL;
char buf[256];
char *p,*q;

fp = fopen("titanic3.csv","rt");
if (fp == NULL)goto end;

buf[0] = 0;
fgets(buf, sizeof(buf), fp);
while (1)
{
buf[0] = 0;
fgets(buf, sizeof(buf), fp);
if (buf[0] == 0)break;
for (i = 0; i < 11; i++){
p = buf;
if (i > 0)p = NULL;
q=strtokrev(p, ',');
if (q == NULL)q = "";
strcpy(list[c][i], q);
}
c++;
}
#if 0
for (i = 0; i < c; i++) {
printf("%d,%s,%s,\"%s\"\n",
892+i,list[i][0], list[i][1], list[i][2]);
}
#endif
end:
if (fp)fclose(fp);
}

void read_list2()
{
int i, j, c = 0;
FILE *fp = NULL;
char buf[256];
char *p, *q;

fp = fopen("test.csv", "rt");
if (fp == NULL)goto end;

buf[0] = 0;
fgets(buf, sizeof(buf), fp);
while (1)
{
buf[0] = 0;
fgets(buf, sizeof(buf), fp);
if (buf[0] == 0)break;
for (i = 0; i < 11; i++) {
p = buf;
if (i > 0)p = NULL;
q = strtokrev(p, ',');
if (q == NULL)q = "";
strcpy(list2[c][i], q);
}
c++;
}
#if 0
for (i = 0; i < c; i++) {
printf("%d,%s,%s,\"%s\"\n",
892 + i, list2[i][0], list[i][1], list[i][2]);
}
#endif
end:
if (fp)fclose(fp);
}


void print_test_list()
{
int i, j, pos;
printf("PassengerId,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked\n");
for (i = 0; i < 2000; i++) {
if (list2[i][2][0] == 0)break;
pos = -1;
for (j = 0; j < 2000; j++) {
if (strcmp(list2[i][2], list[j][2]) == 0 && strcmp(list2[i][7], list[j][7]) == 0) {
if (pos != -1) {
printf("okasii1!!!\n");
exit(1);
}
pos = j;
}
}
if (pos == -1) {
printf("okasii2!!!\n");
exit(1);
}
printf("%d,%s,\"%s\","
"%s,%s,%s,%s,"
"%s,%s,%s,%s"
"\n",
892 + i, list[pos][0], list[pos][2],
list[pos][3], list[pos][4], list[pos][5], list[pos][6],
list[pos][7], list[pos][8], list[pos][9], list[pos][10]
);
}

}

void print_result_list()
{
int i, j, pos;
printf("PassengerId,Survived\n");
for (i = 0; i < 2000; i++) {
if (list2[i][2][0] == 0)break;
pos = -1;
for (j = 0; j < 2000; j++) {
if (strcmp(list2[i][2], list[j][2]) == 0 && strcmp(list2[i][7], list[j][7]) == 0) {
if (pos != -1) {
printf("okasii1!!!\n");
exit(1);
}
pos = j;
}
}
if (pos == -1) {
printf("okasii2!!!\n");
exit(1);
}
printf("%d,%s\n",892 + i, list[pos][1]);
}

}

int main()
{
read_list();
read_list2();
//print_test_list();
print_result_list();
}
------------------------------------



2020年1月8日水曜日

KaggleのTitanicの問題をやってみた。

おじさんこんなサイトを見つけました。
https://nehori.com/nikki/2019/12/09/kaggle/
どっかの保田さんって人が書いてんのね。
あらー世の中にはこんなかわいそうな人がいるようです。


このうさんくさそうなサイトの話では、どうもkaggleというとこの問題を解くと、なんと3億円がもらえるらしい。

これは凄い。
ということで今日は上記のサイトにもある、kaggleの入門編のタイタニックの生存者を当てる機会学習をやってみたいとおもいます。

と言っても、これは単なる〇×予想。
なんか問題を見る限りデータをそのまま、svmに入れれば良いんじゃね?
入門編だし、これならおじさんもサクサクできる!

でも、おじさんのこのブログ、c/c++縛りなのです。
なので、今日はこれをc++でといてみたいと思います。
以前c++のsvmの記事をこのブログに書いたし。

戦略として、とりあえずC言語でsvmlight用にデータを整形してsvmlightに食わしてみます。
実装してわかったんですが、データの欠損があるし、正規化とか特徴量抽出とかが意外とめんどくさい。

最初、一発目の手抜き実装で実験をしたら、実行時間が3分くらいかかり、さらに7割くらい正解しているお手本の結果より、さらに正解率が66%違うらしい。
これってひょっとして全然違うのね。
なんという屈辱。あちゃー。

この問題本当によくできているね。
Gooogleとかのプログラミングコンテストと同じで、バカ正直に問題を解こうとすると非常に時間がかかるのね。
おじさん、ひっかかってしまった。

まぁ機械学習って、まず入力の引数が多いと指数関数的に計算時間がかかるので、デバッグを行いやすくするためにもとにかく次元を減らします。
11次元ある入力を重要そうな6次元に絞って計算してみました。
ここでC/C++の威力がでてくるのです。
学習の計算時間が0.2秒くらいになります。

これで計算が早くなったので、次に計算結果が正確になるようにいろいろ試していきます。
誰でも気付くのは性別がけっこう重要。でもこれはもうすでに入力している・・・。
ということでネットでいろいろ検索。

まず、年齢を正規化しないといけないらしい。
ということで年齢を正規化。
だいぶお手本と近くなってきた。
これ、運賃も正規化しないといけないんじゃない?

さらにネットを見る限り、名前に「Mr.」がついてない紳士でないおっさんに「悪い奴だ」フラグを立てると認識率が上がるらしい。
おじさんの周りには苗字に「き」「む」「ら」や「や」「じ」「ま」がついている人は大体極悪人です。
組織がでかくなるとどこにでもいるんだよねぇ、どさくさにまみれてこっそりと悪いことをして、自分だけ生き残ろうとする悪人のおっさん。

ビッグデータってこういう悪いことをするおっさんがいることまでわかるんですね。
おじさん、こういう勝手なゴシップ的な根拠のない人のうわさ的な法則を見つけるのが大好きです。
ということで、悪いおっさんの次元を追加して7次元で実装してみました。

-------------------------
........
OK  1.000000  0.831034
OK  1.000000  1.000158
OK  1.000000  1.094007
OK  1.000000  1.008878
OK  -1.000000  -1.119815
OK  1.000000  1.363857
OK  -1.000000  -1.164235
OK  -1.000000  -1.119815
OK  -1.000000  -0.351570
ct=418  ok=403  96.411483%
-------------------------

おー、やっとお手本と96%一致。
まだ4次元しか正しく入力を合わせてないのに、なんか結果がよすぎる。
なんか正解率の計算間違ってんのかなぁ?

調べて分かったことは、お手本だと思っていたデータはとりあえず男が全員死亡、女が成員生存のダミーデータらしい。
でもとりあえずこれでやっと計算にはバグがなさそうなことがわかりました。

この後は、自分の回答とお手本のどちらが正しいかは回答を実際にコミットしてみないとわからいです。
ここから、何度もWebサイトに結果のコミットを繰り返さないと、正しい答えがわからいのですが、時間がないので今回はここまで。

まだ使っていない残りの次元が7次元あるので、これらに対して入力を正しく行うと正解率がもっと上がると思うんですが、100%正解のデータがダウンロードできないので実験をやるのがめんどくさい。

なんかこの課題は認識率80%を超えると挑戦者の上位3%に入ってすごいらしいです。
僕の回答は良い方向にくるっているのでしょうか?それとも悪い方向にくるってるのでしょうか?

今日はKaggleはPythonやKerasをインストールしなくてもC/C++言語だけでもできるよという話でした。

------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static char *strtokrev(char *s1, int s2) {
static char *str = 0;
int f = 0;

if (s1)str = s1;
else s1 = str;

if (!s1)return NULL;

while (1) {
if (!*str) {
str = 0;
return(s1);
}
if (*str == '\r' || *str == '\n') {
*str = 0;
str = 0;
return(s1);
}
if (*str == '\"' && f == 0) {
str++;
s1 = str;
f = 1;
continue;
}
if (*str == '\"' && f == 1 && *(str + 1) == '\"') {
str += 2;
continue;
}
if (*str == '\"' && f == 1) {
*str++ = 0;
f = 0;
continue;
}
if (*str == s2 && f == 0) {
*str++ = 0;
return(s1);
}
str++;
}
}


struct person{
int id;
int survive;
int cls;
char name[64];
int sex;
int age;
double s_age;
int sib;
int per;
int f_mr;
double fare;
};

struct person{
int id;
int survive;
int cls;
char name[64];
int sex;
int age;
double s_age;
int sib;
int per;
int f_mr;
double fare;
};

struct person pli[1024];
struct person pli2[1024];

void store(struct person* plist,int pos, int n, const char* str)
{
char buf[256];
switch (n)
{
case 0:
sscanf(str, "%d", &(plist[pos].id));
break;
case 1:
sscanf(str, "%d", &(plist[pos].survive));
if (plist[pos].survive == 0)plist[pos].survive = -1;
break;
case 2:
sscanf(str, "%d", &(plist[pos].cls));
break;
case 3:
if (strlen(str) < 64 - 1)strcpy(plist[pos].name, str);
if (strstr(plist[pos].name, "Mr."))plist[pos].f_mr = 1;
else plist[pos].f_mr = -1;
break;
case 4:
buf[0] = 0;
plist[pos].sex = -1;
if (strlen(str) < 64 - 1)strcpy(buf, str);
if(strstr(buf,"female"))plist[pos].sex = 1;
else if (strstr(buf, "male"))plist[pos].sex = -1;
break;
case 5:
plist[pos].age = -1;
sscanf(str, "%d", &(plist[pos].age));
break;
case 6:
sscanf(str, "%d", &(plist[pos].sib));
break;
case 7:
sscanf(str, "%d", &(plist[pos].per));
break;
case 9:
sscanf(str, "%lf", &(plist[pos].fare));
break;
default:
break;
}
}
int read_plist(struct person* plist,const char* fn,int ff)
{
FILE *fp=NULL;
char buf[256];
int i, f, id, re, ct = 0;
char *p,*q;

int ct2=0;
static double ave,dmax = 0, dmin = 1000,sub=0;

int fct2=0;
static double fave, fdmax = 0, fdmin = 1000, fsub = 0;

fp = fopen(fn,"rb");
if (fp == NULL)goto end;
fgets(buf, sizeof(buf), fp);
while (1) {
buf[0] = 0;
fgets(buf, sizeof(buf), fp);
if (buf[0] == 0)break;
for (i = 0; i < 14; i++) {
p = buf;
if (i != 0)p = NULL;
if (ff && i == 1)continue;
q = strtokrev(p, ',');
if (q == NULL)q = "";
store(plist,ct,i, q);
}
ct++;
}
if (ff == 0) {
for (i = 0; i < ct; i++) {
if (plist[i].age == -1)continue;
ct2++;
sub += plist[i].age;
if (plist[i].age > dmax)dmax = plist[i].age;
if (plist[i].age < dmin)dmin = plist[i].age;
}
ave = sub; ave /= ct2;
for (i = 0; i < ct; i++) {
if (plist[i].fare == 0)continue;
fct2++;
fsub += plist[i].fare;
if (plist[i].fare > fdmax)fdmax = plist[i].fare;
if (plist[i].fare < fdmin)fdmin = plist[i].fare;
}
fave = fsub; fave /= fct2;
}
//printf("ave=%f\n",ave);
//printf("fave=%f\n",fave);
for (i = 0; i < ct; i++) {
if (plist[i].age == -1)plist[i].s_age = ave;
else plist[i].s_age = plist[i].age;

plist[i].s_age = (plist[i].s_age - dmin)*2/dmax-1;
plist[i].fare = (plist[i].fare - fdmin) * 2 / fdmax - 1;
}

end:
if(fp)fclose(fp);
return 0;
}

void print_plist(struct person* plist,FILE* fd)
{
int i;
if (fd == NULL)return;
for (i = 0; i < 1024; i++) {
if (plist[i].id == 0)break;
fprintf(fd,"%d 1:%d 2:%d 3:%d 4:%f 5:%d 6:%d 7:%f\n",
plist[i].survive,plist[i].cls,plist[i].f_mr,plist[i].sex,plist[i].s_age,
plist[i].sib,plist[i].per,plist[i].fare);
}
}

void comp(const char* fn1,const char* fn2)
{
FILE *f1 = NULL, *f2 = NULL;
char buf1[256];
char buf2[256];
int ct = 0;
int ok = 0;
double d1, d2;
int id, f;
double per;
f1 = fopen(fn1, "rt");
f2 = fopen(fn2, "rt");
if (f1 == NULL || f2 == NULL)goto err;

fgets(buf1, sizeof(buf1), f1);
while (1) {
buf1[0] = 0;
buf2[0] = 0;
d1 = 0;
d2 = 0;
f = 0;
fgets(buf1, sizeof(buf1), f1);
fgets(buf2, sizeof(buf2), f2);
if (buf1[0] == 0 || buf2[0] == 0)break;
ct++;
sscanf(buf1, "%d,%lf", &id, &d1);
sscanf(buf2, "%lf", &d2);
if (d1 == 0)d1 = -1;

if (d1 > 0 && d2 > 0)f = 1;
if (d1 < 0 && d2 < 0)f = 1;
if (f)ok++;
printf("%s  %lf  %lf \n", f ? "OK" : "NG", d1, d2);
}
per = ok * 100;
per /= ct;
printf("ct=%d  ok=%d  %f%%\n", ct, ok, per);
err:
if (f1)fclose(f1);
if (f2)fclose(f2);
}

int main()
{
FILE* fp;
read_plist(pli,"train.csv",0);
read_plist(pli2,"test.csv",1);
fp = fopen("yomei_train.csv","wb");
print_plist(pli,fp);
if (fp)fclose(fp);
fp = fopen("yomei_test.csv", "wb");
print_plist(pli2, fp);
if (fp)fclose(fp);
//printf("----------pli\n");
//print_plist(pli, stdout);
//printf("----------pli2\n");
//print_plist(pli2, stdout);
system("svmlight_learn yomei_train.csv yomei_model.txt");
system("svmlight_classfy yomei_test.csv yomei_model.txt yomei_result.csv");
comp("gender_submission.csv", "yomei_result.csv");
return 0;
}
-------------------------