これまでのあらすじ:
2016年3月、フェルト生地を手で裁断している際にレーザーカッターがあれば複雑なカットが容易にできるなあと思って、安価になってきたレーザーカッターを購入しようと思ったのがきっかけ。調べていくうちに、合板も切れたほうがいいと思うようになって、CNCルーター(CNCミリング)についても考えるようになった。
Arduinoは以前から使っており、CNCシールドがあると気付いて自作も可能と思うようになった。当初はShapeOkoやX-CARVEを参考にMakerSlide、OpenRail、V-Wheel、2GTタイミングベルトなどで5万円くらいで自作しようと思っていた。AliExpressでも部品が安く買えることが分かって、しばらくは部品探し。探せば探すほど安くて本格的な部品も見つかってくるので、そんなにケチらなくてもいいのではないかと徐々にスペックアップ。最終的には剛性や精度のことも考えてボールスクリューやリニアスライドを使うことになり、予想以上に重厚な3軸CNCマシンをつくることに(約7万円)。
構想から約5週間(制作約3週間)でルーターとレーザーともに使えるようになり、現在はgrbl1.1+Arduino CNCシールドV3.5+bCNCを使用中(Macで)。余っていたBluetoothモジュールをつけてワイヤレス化。bCNCのPendant機能でスマホやタブレット上のブラウザからもワイヤレス操作可能。
その他、電子工作・プログラミング、最近は機械学習などもやっています。基本、Macを使っていますが、機械学習ではUbuntuを使っています。


CNCマシン全般について:
国内レーザー加工機と中国製レーザー加工機の比較
中国製レーザーダイオードについて
CNCミリングマシンとCNCルーターマシンいろいろ
その他:
利用例や付加機能など:
CNCルーター関係:



*CNCマシンの制作記録は2016/04/10〜の投稿に書いてあります。

2017年6月9日金曜日

AliExpressのトラブル(その2)

以前、AliExpressで注文した商品が結局届かなくて払い戻ししてもらいましたが、また違ったトラブルがありました。あいかわらず、数百円程度の電子パーツなどをよく注文しており、もう100回以上なにかしら注文しています。実はここ最近で3回ほどトラブルがありました。

(1)商品が期日内に届かなかった(再配送で解決済)
(2)商品のスペックが違う(全額返金済み/商品は返品せず)
(3)商品が期日内に届かない(現在連絡中、追記:結局Open Disputeで返金)

という感じです。
以下順に書いていきます。


(1)商品が期日内に届かなかった場合:
まずは以下のようなUSB接続可能な小型マイクを注文しました。
AliExpress.com Product - Portable Studio Speech Super Mini USB 2.0 Microphone MIC Audio Adapter Driver Free for MSN PC Notebook Lectures Teaching 202円(送料込み)このショップで購入したわけではないのですが、当時の購入金額は159円(送料込み)でした。3/17に注文して約2ヶ月の期日が過ぎても届かないため、まずはショップにメールしました。今回は特別急いでいなかったので(というよりも注文してから2ヶ月も経ってしまい、代替のUSBマイクを使用することにしたため)、いきなりOpen Disputeをせずに連絡してみることにしました。ちなみに、トラッキング状況は以下のような感じで、途中で止まっているように見えます。配送中に紛失してしまったのかどうかはわかりません。

これもまた一番安い通常郵便で配送されているために、きちんとしたトラッキングサービスはカバーされていないはずなので、そもそもあまり当てになりません。もしきちんとしたトラッキングサービスにするには、以下のような輸送オプションがあり、141円上乗せすればAir Mailなのでトラッキングならびに受取り確認(要ハンコ)になります。しかし、今回は安い買い物なのでわざわざそこまではしません。

そのときのやりとりが以下です(やりとりは下から始まって上へ)。

やりとりの内容を書くと、Me:「まだ届いてないけど、返金するためOpen Disputeしていいですか?」Shop:「Open Disputeはしないで下さい。新しいの送るかPaypal経由で返金もできます。」Me:「じゃ、新しいの送って下さい」Shop:「OK、すぐに手配します」Me:「送る時教えてね。今回トラッキング番号とかある?」Shop:「新しいトラッキング番号にしておくので安心してください」という感じでやりとりは進んで、
Shop:「新しいのを送りました。トラッキング番号は〜です。遅れてすみません。なにかあれば連絡下さい。」という感じで、商品を梱包した状態の写真を送ってきてくれたので、大丈夫だろうと信じて、これでやりとり終了。あとはちゃんと届くかどうか。そして、約2週間後無事届きました(いわゆる通常輸送にかかる時間で)。結局、注文して届くまでから2ヶ月半くらいかかったというわけです。
追記:実は、最初に注文したときのUSBマイクが、約4ヶ月後に届きました。つまり、最終的には合計2個届いたということになります。封筒にスタンプされた日時を確認すると、注文後約3ヶ月半くらい中国国内の集配センターのようなところにあったようです。封筒(小包)が10cm角ほどしかなく小さいので、一時的にどこかに紛れ込んで行方不明になっていたのかもしれません。

(2)の商品のスペックが違った場合:リチウム充電池18650です。もともと怪しいのはわかっていたのですが、試しに注文してみたという感じです。
AliExpress.com Product - 2PCS 3.7 V 4200mAh 18650 Battery batteries batteria lithium Li Ion Rechargeable Large Capacity T6 Flashlight free shipping 404円(2本:送料無料)これは4200mAhもあると書かれているリチウム充電池です。この容量に対して一本約200円という値段(安すぎ)。この手の容量をごまかしている18650リチウム充電池は沢山あるようです。以下は、Youtubeで容量を検証している動画です。こんな動画が沢山あります。

今回注文したものはMick Tick 18650で、注文時の価格は2本で399円でした。2週間程度で届いたので配送においては問題ありませんでした。容量が怪しいので、受け取ってもすぐに受取ボタンを押さずに、以下の充電器で計測してみました。
AliExpress.com Product - Original XTAR VC2 2 Slots 1A Current Universal Intelligent USB Battery Charger with LCD Display for 3.6V / 3.7V Li-ion 18650 1566円(送料無料)この充電器では、充電した分のmAhを数値で表示してくれます。なので、いったんバッテリーを放電してから充電してみました。
結果はこの通りです↑。左側が539mAh、右が443mAh。Youtubeの動画などでも、偽物はだいたい500mAhくらいしかないようです。案の定、今回のバッテリーも同様でした。ついでに重さも計測してみると、

左が24.9g、右が24.5gです。きちんとしたものならば、40g以上あるそうです。
これも動画にあったような偽物の特徴と一致しています。

ということで、今回は問答無用でOpen Disputeボタンを押して、これらの画像も証拠写真として添付し、早速クレームをつけました。
まずは、先程の計測結果内容を書き、全然4200mAhではないので、399円中の350円を返金しろと(画像も添えて)。返品すると送料などかかってしまうため、一部返金ということにしました。
すると数日後に以下のような返信。
Disputeする理由を「商品のキャパシティーが違うため」を選んでいたのですが、ショップ側は「買い手の個人的な理由で」に変更して欲しいとのこと。「注文したけど、思っていたものと違うため」というような理由でキャンセルするということにしてくれれば、返金しますと書いてありました。
このことから察すると、ショップとしても理由によってはAliExpressから何らかのペナルティを受けてしまうのを恐れているのでしょう。
返金してくれるということなので、ためしに言われたとおりにDispute理由を変更し、ついでに399円全額返金希望にしました。

「言われたとおり変更したよ」とメールでも送っておき、そうするとショップ側はこの条件を受け入れたようで無事解決。
ショップは「返金手続きしておきました。悪い評価しないでね。」という控えめな姿勢のメール。

結果としては399円支払った分、全額返金。そして18650のリチウム充電池2本(実際の容量は500mAh前後)は返品不要となりました。
ちなみに、単3アルカリ電池ですら約1000mAhあるらしいので、500mAというのはマンガン電池程度という感じです。ちょっとしたものに使うのならいいけれども、あまりにも容量が少なすぎです。
ということから、18650などのリチウム充電池を購入する際は注意が必要です。やはり日本製が高価ではあるけれどもいいのかもしれません。
トラブルはないほうがいいですが、もしこういうトラブルがあってもAliExpressのシステムのせいか、クレームつければ一応きちんと対応してくれます。


(3)の場合:これもまた期限すぎても届かない(もう慣れたかも)
Bluetoothではないワイヤレスマウスもありますが、USBドングルをつけるのは面倒なのでBluetoothのマウスを探していたところ、Amazonだと1000円くらいするのがAliExpressで345円だったので注文してみました。

AliExpress.com Product - Ergonomic Wireless Bluetooth 3.0 mini Optical Computer Mouse 6 Buttons Mice 1600DPI for laptopTablet PC 10M Working distance 656円(送料無料)
注文したのは、このショップのものではないのですが、今はなぜか全般的にBluetoothマウスはAliExpressで650円以上します。当時注文したのは345円でしたが、セールとかだったのでしょうか?このマウスもまた期限(2ヶ月)が過ぎても届かないので、とりあえずメール問い合わせしている最中。いきなりOpen Disputeしてもいいのですが、返金扱いにして新たにBluetoothマウスを購入することになると650円になってしまうので、なんとか345円でそのままもう一度同じマウスを送ってもらったほうがいいのではないかということです。

あいかわらず通常郵便なのでトラッキングサービスはカバーされていません。一応配送状況はこんな感じで出ていますが、4/8以降は途切れたままです。いままでの経験からすると、この状態でも届くものは届くのですが、やはり2ヶ月以上届かないというのは、もう届かないと思ったほうがいいと思います。いままでで、遅くても6週間くらいでした。通常なら2〜3週間。というわけで、これは現在進行中です。
追記:その後、数回のメールのやりとりをして、最終的にはOpen Disputeで返金手続きしました。ということで、また新たにBluetoothマウス(別の製品:378円)を注文し直しました。
AliExpress.com Product - 1600 DPI Bluetooth BT 3.0 Wireless Mouse for Android Phone Computers Notebooks Tablet Office Gaming PC Mouse Mini Mice Newest


まとめ:スペックが怪しい商品、動作確認が必要そうな商品は、受け取ってもすぐに受取ボタンを押さずに、実際に電源につないで動かしてみたほうがいいと思います。そして問題があれば、受取ボタンを押す前にショップへメール連絡したほうがいいでしょう(できれば強気で)。ショップとしても客からクレームつくのは嫌だと思うので、それなりの対応をしてくれると思います。Open Disputeは、まずお互いの条件を出し合い、それでOKならそのまま解決へと向かいますが、どちらかが納得行かない場合は、つぎの段階としてAliExpressが介入してくれるようです。AliExpressメンバーレベルというのがあって、A3レベルだと25ドルまではすぐに返金してくれるようです。A4レベルなら100ドルまで。買い物すればするほどレベルはあがって、様々な特典が得られるようになります。ショップとしてもペナルティにつながるようなことにはなりたくないはずなので(出店禁止とか)、きちんと証拠写真や要望を述べれば大丈夫かと思います。あまりぐだぐだと「このような場合どうしたらいいでしょうか?」というようなお伺いをたてないほうがいいと思います。単純に、「商品が〜だったので(証拠写真付きで)、全額返金希望(あるいは一部返金希望)」と書いた方がいいと思います。
いまのところ110回のショッピングで4回のトラブルがあったということになります。返金や再配送というかたちで解決しているので、特に問題ないという感じです。これにこりてAliExpressではショッピングしなくなるかというと、そういうわけでもありません。AliExpressの場合、2ヶ月経たないとわからないというのが面倒なので、すぐに欲しいものであれば多少高くてもAmazonのほうがいいかもしれません。ただしこのような部品類などはAmazonでも中国からの配送となるので、内容的には同じことだと思います。同じ中国からでも、Amazonのほうが若干早く届くような気がしますが、たまにものすごく遅いときもありました。なので、中国からの配送品に関しては、時間についてはあまり期待しないほうがよさそうです。2週間でつけば早いほうで、1ヶ月くらいは待つつもりでいたほうがいいかもしれません。

2017年6月2日金曜日

線形回帰のプログラミング(その2):勾配降下法

前回、グラフ平面上の複数の点(データ群)に対して直線フィッティングをするために最小二乗法を試してみましたが、今回は勾配降下法によるプログラムです。
最小二乗法は、公式に数値を入れるとすぐに答えがでてくるような感じでしたが、勾配降下法は直線の式に動的に近づいていく求め方なので、コンピューターを利用した求め方に、より適しているような気がします。公式によって一発で答えを導き出すというよりも、経験的に答えを探しにいくという感じでしょうか。

黄直線:勾配降下法、赤直線:最小二乗法

とはいっても、やはり公式があるようで、前回同様求めたい直線の式を

y = m * x + b

とすれば、

Error(m, b) = 1/n * Σ((m * xi + b) - yi)^2

が、求めたい式と暫定的な式とのmとbのトータルの誤差を表しているようです。Σは、i=1からi=nまでの合計値(for文上では、i=0からi=n-1まで)。
式中の (m * xi + b) - yi は、m * xi + bで求めたyの値から、ある点のyiを差し引いているので、その差が誤差(Error)になるというのがわかります。二乗しているのは、前回の最小二乗法のように符号を+にするということですが、そもそも最終的な目的が、誤差の最小化なので二乗してあっても問題ないということらしいです。
1/nのnは点の数なので、Σで合計したエラー値をnで割ることで平均値が得られるという感じでしょうか。
最終的には、このError(誤差)が0に近づくようにmとbを探していけばいいので、そのためにはこのErrorのグラフ上の傾き(接線)を調べると、式中のΣ以降の部分である

((m * xi + b) - yi)^2

を微分して接線(x,y地点の傾き)を調べると

2*((m * xi + b) - yi)

となり、求めたいmとbについての偏微分(変化率)は

Δm = -2/n * Σ xi * ((m * xi + b) - yi)
Δb = -2/n * Σ (m * xi + b) - yi

となって、このΔmとΔbをプログラム上で加算(更新)していくことで、徐々に求めたい式に近づいていくという方法のようです。
暫定的な直線の式にxとyの値を代入して誤差を求めて、その誤差が0になるようにmとbを更新していくというイメージはなんとなく分かるのですが、実際計算上でどのようにそうなっていくのか?このあたりを今回もプログラミングを通して実感できないかという試みです。
数式だけだと分かりにくいのですが、プログラミングでは今回の値に前回の値を再代入して、それを繰り返して徐々にある目標の値に近づいていくという方法はよくあるので、今回もそんなイメージで考えています。

勾配降下法においては、一発で答えを導くわけではないので、学習率という変数が登場してくるようです。少しづつ求めたい値に近づくために、オーバーシュートしないように0.1や0.01を掛けて小刻みにステップさせる係数のようなものだと思います。
さきほどのΔmとΔbの偏微分の方程式を書くと(今回もProcessingを使用)、

float m = 0;
float b = 0;
float learningRate = 0.1;
float db = 0;
float dm = 0;
for(int i = 0; i < x.length; i++){
    dm += x[i] * ((m * x[i] + b) - y[i]);
    db += (m * x[i] + b) - y[i];
}

m -= 2.0/x.length * dm * learningRate;
b -= 2.0/x.length * db * learningRate;

こんな感じになります。ΔmとΔbはdmとdb、学習率はlearningRate=0.1、for文で点の個数分繰り返しΔmとΔbの加算処理をしています。最終的なmとbは、学習率0.1を掛けたΔmとΔbと今回のmとbとの差分を差し引いて、次回また計算し直して徐々に求めたいmとbに近づいていくというプロセスです。偏微分の方程式があるため、それに従って計算してしまえばいいので、今回は結構短いコードでmとbを求めていくことができます。
しかし、最後に求めたmとbの式をよく見ると、適当に決めた学習率(今回の場合0.1)を掛けているので、

m -= dm * learningRate;
b -= db * learningRate;

のように式中の2.0/x.lengthを消してしまっても、あまり大差なさそうです。今回は方程式のまま書いてみましたが、計算をシンプルにするには、learningRateに係数を含ませてもいいと思います。
あるいは、もっと式をシンプルにすれば、

float m = 0;
float b = 0;
float learningRate = 0.1;
for(int i = 0; i < x.length; i++){
    float error = y[i] - (m * x[i] + b);
    m += error * x[i] * learningRate;
    b += error * learningRate;
}

とするだけでもいいのかもしれません。
式中のerrorは、実際のy[i]の値からm*x[i]+bで求めたyの値(もともとy=m*x+bという関係なので)を差し引いた誤差です。
結局は、learingRateで挙動を調整することができるので、ここまで変えてしまっても問題なさそうです。


以下が今回のプログラム(Processing/Processing.js)。前回同様に、以下の空のグラフ上の任意位置をクリックすると点が追加されます。複数の点(2点以上)に対する直線が現れます。 赤の直線は前回の最小二乗法、黄色の直線が今回の勾配降下法によるものです。 点の数や位置によっては、微妙に最小二乗法と勾配降下法の結果がずれることがあります。求め方の違いからくるためなのでしょうか?ブラウザリロードで初期化します。

表示が変な場合(線が点滅するなど)は、前回(一つ前の投稿)のページ上のプログラムと干渉しあっているために起こると思われます。 そうならないようにするためには、この投稿だけのページに切り替えるといいと思います。ここをクリックでこの投稿だけのページへ移動



以下が、全体のコード。
前回と少し違うのは、画面サイズ400x400を一旦0〜1.0に正規化してあります。つまり座標(200,200)の位置をクリックすれば、プログラム内部では(0.5,0.5)という値に相当します。0~1.0で計算しておいて(そうしないと計算の際に数値が溢れてしまったので)、その後画面表示するときは、再度400x400のフォーマットに変換し直しています。

float[] x = new float[0];
float[] y = new float[0];
float m_gd = 0; //勾配降下法のmの変数
float b_gd = 1; //勾配降下法のbの変数
float m_ls = 0; //最小二乗法にmの変数
float b_ls = 0; //最小二乗法にbの変数

void setup(){
  size(400,400);
  rectMode(CORNERS);
}

void draw(){
  background(51); 
  stroke(255);
  fill(255);
  for(int i = 0; i < x.length; i++){//点の描画
    ellipse(x[i]*width,y[i]*height,4,4);
  }
  
  if(x.length > 1){//点が2点以上の場合
    leastSquare();
    gradientDescend();
  }
}

void gradientDescend(){//勾配降下法
  float learningRate = 0.1;
  float db = 0;
  float dm = 0;
  for(int i = 0; i < x.length; i++){  
    dm += x[i] * ((m_gd * x[i] + b_gd) - y[i]);
    db += (m_gd * x[i] + b_gd) - y[i];
  }
  
  m_gd -= 2.0/x.length * dm * learningRate;
  b_gd -= 2.0/x.length * db * learningRate;

  stroke(255,255,0);
  drawLine(m_gd, b_gd);  //勾配降下法による直線描画
}

void leastSquare(){//最小二乗法
  float xsum = 0;
  float ysum = 0;
  for(int i = 0; i < x.length; i++){
    xsum += x[i];
    ysum += y[i];
  }  
  float xmean = xsum / x.length;
  float ymean = ysum / y.length;  
  float xy = 0;
  float xx = 0;
  for(int i = 0; i < x.length; i++){
    xy += (x[i] - xmean) * (y[i] - ymean);
    xx += (x[i] - xmean) * (x[i] - xmean);    
  }  
  m_ls = xy / xx;
  b_ls = ymean - m_ls * xmean;
  
  stroke(255,0,0);
  drawLine(m_ls, b_ls); //最小二乗法による直線描画
}

void drawLine(float M, float B){//mとbによる直線描画
  float x1 = 0;
  float y1 = M * x1 + B;
  float x2 = 1;
  float y2 = M * x2 + B;  
  line(x1 * width, y1 * height, x2 * width, y2 * height);
}

void mousePressed(){//クリックするごとに新たな座標値を配列に追加
  x = append(x, 1.0 * mouseX / width); //0〜1.0に正規化して配列に格納
  y = append(y, 1.0 * mouseY / height);
}


学習率:learningRateは0.1に設定してありますが、0.01に落としてもいいかもしれません。ただその場合、動きはゆっくりになります。
点の数が少なすぎたり、互いに点が近すぎると、最小二乗法による直線(赤)と勾配降下法による直線(黄)とに微妙な違いがでてくるようです。学習率(単位ステップ数)を調整すればいいのかもしれませんが、そのへんについては検討中。

今回の最小二乗法と勾配降下法については、数学的に証明可能なレベルまで理解したり、ここで登場してくる方程式を自ら導き出せるほど完全に理解しているというわけではないのですが、プログラミングにおけるライブラリのように、ライブラリをつくるのは大変だけれども、ある程度の仕組みを理解して使いこなすということはできると思います。画像認識のOpenCVあるいはPID制御やFFT(高速フーリエ変換)などのライブラリもそんな感じです。まったく理解していないとライブラリを使うこともできませんが、そこそこ理解していれば、これまで計算できなかったことが可能になるので、その程度は勉強しておいたほうがいいのかもしれません。もしくは、完全な理解を得るには時間がかかってしまうので(そもそもその分野の専門家でもないので)、深い理解はとりあえずはスキップし、まずは使い方を身につけて、使っているうちに徐々にその仕組がわかってくるという順番のほうがいいのかもしれません(知識欲のために理解しようとしているわけではないので)。

続き:
CourseraのMachine LearningコースとDeep Learningコース

人気の投稿