論理回帰(Logistic Regression)概要及びC++実現

15256 ワード

論理回帰(Logistic Regression):このモデルは回帰ではなく分類に使用され、logitic sigmoid関数を使用することができます.http://blog.csdn.net/fengbingchun/article/details/73848734 )線形関数の出力を圧縮して区間(0,1):
p(y=1|xθ)=σ(θTx)
論理回帰は、一般化された線形回帰分析である(参照可能: http://blog.csdn.net/fengbingchun/article/details/77892193 )モデルは、複数の線形回帰分析と多くの共通点があります.それらのモデルは基本的に同じで、w’x+bを有しています.ここで、wとbは、変数によって異なる、多重線形回帰は直接w’x+bを因変数、つまりy=w’x+bとしています.論理回帰は関数Lを介してw'x+bを暗黙の状態p、p=L(x+b)に対応します.を選択して、pと1 pの大きさによって変数の値を決定します.Lがlogitic関数であれば、logitic回帰です.Lが多項式関数であれば多項式回帰です.
Logistic回帰の原因変数は二種類に分類されてもいいし、多分類されてもいいです.多くの種類はソフトマックス方法を使用できます. http://blog.csdn.net/fengbingchun/article/details/75220591 )処理を行う
論理回帰を最適化する方法は、勾配降下法、ニュートン法、BFGSを含む.最適化の主な目標は方向を見つけることであり、パラメータがこの方向に移動した後、尤然関数の値を減少させることができる.この方向は、一次偏向ガイドまたは二次偏向ガイドの様々な組合せによって求められる.これらの方法は通常、反復的に極小値に近づくことができる.
Logistic回帰は線形回帰モデルと似ていますが、変数が二分割変数のモデルに適用されます.論理回帰には論理関数であるsigmoid関数(論理回帰適合関数)が使用されています.論理回帰における損失関数(cost function)は最も類似した推定に基づいて導出されます.
論理回帰におけるオーバーフィット:論理回帰または線形回帰の損失関数からなるモデルには、大きな重みがあり、小さな重みがあり、モデルの複雑さが向上し、汎化能力が低下することがある.一般的には正規化された方法でオーバーフィットを解くことが多い.
以下はOpenCV 3.3におけるLogisticRegression類を参照して実現される2つの分類論理回帰codeである.トレーニング方法はBatchとMini Batchを含み、簡単な正規化方法をサポートし、コードは以下の通りである.
logic ticgression.hpp:
#ifndef FBC_NN_LOGISTICREGRESSION_HPP_
#define FBC_NN_LOGISTICREGRESSION_HPP_

#include 
#include 
#include 

namespace ANN {

template
class LogisticRegression { // two categories
public:
	LogisticRegression() = default;
	int init(const T* data, const T* labels, int train_num, int feature_length,
		int reg_kinds = -1, T learning_rate = 0.00001, int iterations = 10000, int train_method = 0, int mini_batch_size = 1);
	int train(const std::string& model);
	int load_model(const std::string& model);
	T predict(const T* data, int feature_length) const; // y = 1/(1+exp(-(wx+b)))

	// Regularization kinds
	enum RegKinds {
		REG_DISABLE = -1, // Regularization disabled
		REG_L1 = 0 // L1 norm
	};

	// Training methods
	enum Methods {
		BATCH = 0,
		MINI_BATCH = 1
	};

private:
	int store_model(const std::string& model) const;
	T calc_sigmoid(T x) const; // y = 1/(1+exp(-x))
	T norm(const std::vector& v1, const std::vector& v2) const;
	void batch_gradient_descent();
	void mini_batch_gradient_descent();
	void gradient_descent(const std::vector<:vector>>& data_batch, const std::vector& labels_batch, int length_batch);

	std::vector<:vector>> data;
	std::vector labels;
	int iterations = 1000;
	int train_num = 0; // train samples num
	int feature_length = 0;
	T learning_rate = 0.00001;
	std::vector thetas; // coefficient
	//T epsilon = 0.000001; // termination condition
	T lambda = (T)0.; // regularization method
	int train_method = 0;
	int mini_batch_size = 1;
};

} // namespace ANN

#endif // FBC_NN_LOGISTICREGRESSION_HPP_
logic tic Ingurgression.cpp:
#include "logistic_regression.hpp"
#include 
#include 
#include 
#include 
#include "common.hpp"

namespace ANN {

template
int LogisticRegression::init(const T* data, const T* labels, int train_num, int feature_length,
	int reg_kinds, T learning_rate, int iterations, int train_method, int mini_batch_size)
{
	if (train_num < 2) {
		fprintf(stderr, "logistic regression train samples num is too little: %d
", train_num); return -1; } if (learning_rate <= 0) { fprintf(stderr, "learning rate must be greater 0: %f
", learning_rate); return -1; } if (iterations <= 0) { fprintf(stderr, "number of iterations cannot be zero or a negative number: %d
", iterations); return -1; } CHECK(reg_kinds == -1 || reg_kinds == 0); CHECK(train_method == 0 || train_method == 1); CHECK(mini_batch_size >= 1 && mini_batch_size < train_num); if (reg_kinds == REG_L1) this->lambda = (T)1.; if (train_method == MINI_BATCH) this->train_method = 1; this->mini_batch_size = mini_batch_size; this->learning_rate = learning_rate; this->iterations = iterations; this->train_num = train_num; this->feature_length = feature_length; this->data.resize(train_num); this->labels.resize(train_num); for (int i = 0; i < train_num; ++i) { const T* p = data + i * feature_length; this->data[i].resize(feature_length+1); this->data[i][0] = (T)1; // bias for (int j = 0; j < feature_length; ++j) { this->data[i][j+1] = p[j]; } this->labels[i] = labels[i]; } this->thetas.resize(feature_length + 1, (T)0.); // bias + feature_length return 0; } template int LogisticRegression::train(const std::string& model) { CHECK(data.size() == labels.size()); if (train_method == BATCH) batch_gradient_descent(); else mini_batch_gradient_descent(); CHECK(store_model(model) == 0); return 0; } template void LogisticRegression::batch_gradient_descent() { for (int i = 0; i < iterations; ++i) { gradient_descent(data, labels, train_num); /*std::unique_ptr z(new T[train_num]), gradient(new T[thetas.size()]); for (int j = 0; j < train_num; ++j) { z.get()[j] = (T)0.; for (int t = 0; t < feature_length + 1; ++t) { z.get()[j] += data[j][t] * thetas[t]; } } std::unique_ptr pcal_a(new T[train_num]), pcal_b(new T[train_num]), pcal_ab(new T[train_num]); for (int j = 0; j < train_num; ++j) { pcal_a.get()[j] = calc_sigmoid(z.get()[j]) - labels[j]; pcal_b.get()[j] = data[j][0]; // bias pcal_ab.get()[j] = pcal_a.get()[j] * pcal_b.get()[j]; } gradient.get()[0] = ((T)1. / train_num) * std::accumulate(pcal_ab.get(), pcal_ab.get() + train_num, (T)0.); // bias for (int j = 1; j < thetas.size(); ++j) { for (int t = 0; t < train_num; ++t) { pcal_b.get()[t] = data[t][j]; pcal_ab.get()[t] = pcal_a.get()[t] * pcal_b.get()[t]; } gradient.get()[j] = ((T)1. / train_num) * std::accumulate(pcal_ab.get(), pcal_ab.get() + train_num, (T)0.) + (lambda / train_num) * thetas[j]; } for (int i = 0; i < thetas.size(); ++i) { thetas[i] = thetas[i] - learning_rate / train_num * gradient.get()[i]; }*/ } } template void LogisticRegression::mini_batch_gradient_descent() { const int step = mini_batch_size; const int iter_batch = (train_num + step - 1) / step; for (int i = 0; i < iterations; ++i) { int pos{ 0 }; for (int j = 0; j < iter_batch; ++j) { std::vector<:vector>> data_batch; std::vector labels_batch; int remainder{ 0 }; if (pos + step < train_num) remainder = step; else remainder = train_num - pos; data_batch.resize(remainder); labels_batch.resize(remainder, (T)0.); for (int t = 0; t < remainder; ++t) { data_batch[t].resize(thetas.size(), (T)0.); for (int m = 0; m < thetas.size(); ++m) { data_batch[t][m] = data[pos + t][m]; } labels_batch[t] = labels[pos + t]; } gradient_descent(data_batch, labels_batch, remainder); pos += step; } } } template void LogisticRegression::gradient_descent(const std::vector<:vector>>& data_batch, const std::vector& labels_batch, int length_batch) { std::unique_ptr z(new T[length_batch]), gradient(new T[this->thetas.size()]); for (int j = 0; j < length_batch; ++j) { z.get()[j] = (T)0.; for (int t = 0; t < this->thetas.size(); ++t) { z.get()[j] += data_batch[j][t] * this->thetas[t]; } } std::unique_ptr pcal_a(new T[length_batch]), pcal_b(new T[length_batch]), pcal_ab(new T[length_batch]); for (int j = 0; j < length_batch; ++j) { pcal_a.get()[j] = calc_sigmoid(z.get()[j]) - labels_batch[j]; pcal_b.get()[j] = data_batch[j][0]; // bias pcal_ab.get()[j] = pcal_a.get()[j] * pcal_b.get()[j]; } gradient.get()[0] = ((T)1. / length_batch) * std::accumulate(pcal_ab.get(), pcal_ab.get() + length_batch, (T)0.); // bias for (int j = 1; j < this->thetas.size(); ++j) { for (int t = 0; t < length_batch; ++t) { pcal_b.get()[t] = data_batch[t][j]; pcal_ab.get()[t] = pcal_a.get()[t] * pcal_b.get()[t]; } gradient.get()[j] = ((T)1. / length_batch) * std::accumulate(pcal_ab.get(), pcal_ab.get() + length_batch, (T)0.) + (this->lambda / length_batch) * this->thetas[j]; } for (int i = 0; i < thetas.size(); ++i) { this->thetas[i] = this->thetas[i] - this->learning_rate / length_batch * gradient.get()[i]; } } template int LogisticRegression::load_model(const std::string& model) { std::ifstream file; file.open(model.c_str(), std::ios::binary); if (!file.is_open()) { fprintf(stderr, "open file fail: %s
", model.c_str()); return -1; } int length{ 0 }; file.read((char*)&length, sizeof(length)); thetas.resize(length); file.read((char*)thetas.data(), sizeof(T)*thetas.size()); file.close(); return 0; } template T LogisticRegression::predict(const T* data, int feature_length) const { CHECK(feature_length + 1 == thetas.size()); T value{(T)0.}; for (int t = 1; t < thetas.size(); ++t) { value += data[t - 1] * thetas[t]; } return (calc_sigmoid(value + thetas[0])); } template int LogisticRegression::store_model(const std::string& model) const { std::ofstream file; file.open(model.c_str(), std::ios::binary); if (!file.is_open()) { fprintf(stderr, "open file fail: %s
", model.c_str()); return -1; } int length = thetas.size(); file.write((char*)&length, sizeof(length)); file.write((char*)thetas.data(), sizeof(T) * thetas.size()); file.close(); return 0; } template T LogisticRegression::calc_sigmoid(T x) const { return ((T)1 / ((T)1 + exp(-x))); } template T LogisticRegression::norm(const std::vector& v1, const std::vector& v2) const { CHECK(v1.size() == v2.size()); T sum{ 0 }; for (int i = 0; i < v1.size(); ++i) { T minus = v1[i] - v2[i]; sum += (minus * minus); } return std::sqrt(sum); } template class LogisticRegression; template class LogisticRegression; } // namespace ANN
テストコード:
#include "funset.hpp"
#include 
#include "perceptron.hpp"
#include "BP.hpp""
#include "CNN.hpp"
#include "linear_regression.hpp"
#include "naive_bayes_classifier.hpp"
#include "logistic_regression.hpp"
#include "common.hpp"
#include 

// ================================ logistic regression =====================
int test_logistic_regression_train()
{
	const std::string image_path{ "E:/GitCode/NN_Test/data/images/digit/handwriting_0_and_1/" };
	cv::Mat data, labels;

	for (int i = 1; i < 11; ++i) {
		const std::vector<:string> label{ "0_", "1_" };

		for (const auto& value : label) {
			std::string name = std::to_string(i);
			name = image_path + value + name + ".jpg";

			cv::Mat image = cv::imread(name, 0);
			if (image.empty()) {
				fprintf(stderr, "read image fail: %s
", name.c_str()); return -1; } data.push_back(image.reshape(0, 1)); } } data.convertTo(data, CV_32F); std::unique_ptr tmp(new float[20]); for (int i = 0; i < 20; ++i) { if (i % 2 == 0) tmp[i] = 0.f; else tmp[i] = 1.f; } labels = cv::Mat(20, 1, CV_32FC1, tmp.get()); ANN::LogisticRegression lr; const float learning_rate{ 0.00001f }; const int iterations{ 250 }; int reg_kinds = lr.REG_DISABLE; //ANN::LogisticRegression::REG_L1; int train_method = lr.MINI_BATCH; //ANN::LogisticRegression::BATCH; int mini_batch_size = 5; int ret = lr.init((float*)data.data, (float*)labels.data, data.rows, data.cols/*, reg_kinds, learning_rate, iterations, train_method, mini_batch_size*/); if (ret != 0) { fprintf(stderr, "logistic regression init fail: %d
", ret); return -1; } const std::string model{ "E:/GitCode/NN_Test/data/logistic_regression.model" }; ret = lr.train(model); if (ret != 0) { fprintf(stderr, "logistic regression train fail: %d
", ret); return -1; } return 0; } int test_logistic_regression_predict() { const std::string image_path{ "E:/GitCode/NN_Test/data/images/digit/handwriting_0_and_1/" }; cv::Mat data, labels, result; for (int i = 11; i < 21; ++i) { const std::vector<:string> label{ "0_", "1_" }; for (const auto& value : label) { std::string name = std::to_string(i); name = image_path + value + name + ".jpg"; cv::Mat image = cv::imread(name, 0); if (image.empty()) { fprintf(stderr, "read image fail: %s
", name.c_str()); return -1; } data.push_back(image.reshape(0, 1)); } } data.convertTo(data, CV_32F); std::unique_ptr tmp(new int[20]); for (int i = 0; i < 20; ++i) { if (i % 2 == 0) tmp[i] = 0; else tmp[i] = 1; } labels = cv::Mat(20, 1, CV_32SC1, tmp.get()); CHECK(data.rows == labels.rows); const std::string model{ "E:/GitCode/NN_Test/data/logistic_regression.model" }; ANN::LogisticRegression lr; int ret = lr.load_model(model); if (ret != 0) { fprintf(stderr, "load logistic regression model fail: %d
", ret); return -1; } for (int i = 0; i < data.rows; ++i) { float probability = lr.predict((float*)(data.row(i).data), data.cols); fprintf(stdout, "probability: %.6f, ", probability); if (probability > 0.5) fprintf(stdout, "predict result: 1, "); else fprintf(stdout, "predict result: 0, "); fprintf(stdout, "actual result: %d
", ((int*)(labels.row(i).data))[0]); } return 0; }
トレーニングデータセットはMNISTからです.http://blog.csdn.net/fengbingchun/article/details/49611549  )のtrainでランダムに選択された0、1の各10の画像、テストデータセットはMNISTのtestからランダムに選択された0、1の各10の画像です.ここで、第1列の最初の10個はトレーニングに使い、後の10個はテストに使います.第2列の前の10個はトレーニングに使い、後の10個はテストに使います.

テストの結果は以下の通りです.
逻辑回归(Logistic Regression)简介及C++实现_第1张图片
実行結果から、テスト画像は全て正しく認識され、OpenCV 3.3の結果と一致することがわかった.
GitHub:https://github.com/fengbingchun/NN_Test