深度學習實踐(一)——logistic regression
一、準備
為了更深入的理解logistic regression,筆者基本採用純C++的手寫方式實現,其中矩陣方面的運算則呼叫opencv,資料集則來自公開資料集a1a。
實驗環境:
關於配置方面的操作,請參考一下連結:Win10下OpenCV環境搭建(VS2017+OpenCV3.2.0)
二、logistic regression理論基礎
如果想系統的瞭解logistic regression,筆者推薦吳恩達的深度學習系列課程,尤其是其中的實踐作業,需要認真做。
下面筆者簡略的介紹下logistic regression。
如上圖就是一個logistic regression的典型例子:
- 一張貓的圖片根據rgb可以看成是0-255的之間的數字,所以圖片就轉換成為了一列向量X。
- 定義一個維度為(1,X0)維度的引數W行向量,其中X0指圖片列向量的行數。
- 將W和X相乘(矩陣相乘),再加上偏置b(為實數),則得到Z。
- 再用sigmoid進行限制到(0,1)範圍,輸出A。
- 定義loss,並使用梯度下降演算法,更新引數W和b,使A的輸出越來越接近標籤Y。
下面是一些基本公式:
For one example
The cost is then computed by summing over all training examples:
sigmoid是限制輸出的結果在(0,1)內,它的影象如下:
上面loss的公式採用交叉熵代價函式。
梯度下降演算法:
梯度下降的一個最直觀的解釋:可以看成從山上走下山的過程。
參考連結:
三、實踐
筆者採用的是a1a資料集,其原型為UCI的Adult Data Set ,其大概意思是根據人的特徵來判斷你是否每年的工資大於50k,所以這是一個二分分類問題。
a1a資料集對其進行了簡化,其一共有123個特徵,如下所示為其一行的資料-1 5:1 6:1 17:1 21:1 35:1 40:1 53:1 63:1 71:1 73:1 74:1 76:1 80:1 83:1
,其中-1表示未能超過50k(即負類,實際程式設計可以置為0),接著我們可以初始化一個一行零向量(1,123),5:1表示第5個位置為1,以下類推……這樣我們對其資料就有了個大概認識。
接著我們就開始編寫處理資料的函式。這裡需要一些基礎知識,可以參考以下部落格:
void creatMat(Mat &x,Mat &y,String fileName) {
int line_count = 0;//記錄行數,在矩陣賦值時起到用處
char buffer[256];//快取區
ifstream in(fileName);//定義讀取檔案資料流
if (!in.is_open()) {
cout << "Error opening file"; exit(1);
}
while (!in.eof())
{
in.getline(buffer, 100);//按行讀取
//因為讀取的是字串,下面採用stringstream和atof()將字串轉為浮點數
stringstream stream;
stream << buffer;
string temp_s;//這裡的目的主要是跳過空格
stream >> temp_s;
double num1 = atof(temp_s.c_str());//num1為類別標籤即-1或+1
if (num1 == 1.0) {
y.at<double>( 0,line_count) = num1;//y矩陣即為標籤矩陣,其已經被初始化為0,所以只要將1的標籤賦值即可
}
while (stream >> temp_s) {
int index = temp_s.find(':');
string temp1_s = temp_s.substr(0, index);//這裡模仿split()函式
double t1 = atof(temp1_s.c_str());
string temp2_s = temp_s.substr(index + 1, temp_s.length());
double t2 = atof(temp2_s.c_str());
x.at<double>(t1-1,line_count) = t2;//賦值
}
line_count++;
}
}
然後我們開始編寫sigmoid公式,因為C++和opencv都不帶這個公式。公式為:
Mat sigmoid(const Mat &original) {
cv::Mat response = original.clone();//防止未初始化和維度不同
double temp;
for (int i = 0; i < original.rows; i++) {
for (int j = 0; j < original.cols; j++) {
temp = original.at<double>(i, j);
response.at<double>(i, j) = 1.0 / (1.0 + exp(-temp));
}
}
return response;
}
我們繼續開始編寫cost,公式如下:
其中還需用到對矩陣的log,程式碼如下:
Mat change_log(const Mat &original) {
cv::Mat response = original.clone();//防止未初始化和