Easyx图形库应用(基础的AI开发原理)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
AI已经火了很多年。从早期的cnn、rnn,到现在的chatgpt,ai确实对我们的世界带来了很大的改变。就拿图像处理来说,从早期还不成熟的人脸识别,到现在到处都在使用的二维码识别、车牌识别、身份证识别、人脸识别,ai扮演着非常重要的角色。所以,对于做图像的同学来说,其实有必要知道AI原理是什么。

1、构建基本方程y=f(x)
这个方程有输入,有输出。比如在图上有一群散列点,我们凭经验判断它是直线。那么这个方程就是y=kx+b。这是比较简单的例子。如果难一点,那就是深度学习里面的多层神经网络堆起来的方程y=f1(x) * f2(x) * ......。这些神经网络里面有卷积、有激励、有池化函数、有全连接,无非它们是非线性、是数量更多的子函数堆砌起来的而已。
不同的深度模型,其实就是构建不同的基本方程。反正都是非线性的,是不是真的对,就看训练后和现实的匹配度了。
2、模型就是参数的集合
前面我们说到用直线去拟合一些数据,那么直线有很多种。但是一旦确定了k、b,其实直线就准备好了。所以k和b就是我们想要得到的参数。图像的话,则复杂一些,前面所有神经网络里面的子函数,都存在参数,这些参数堆积在一起,就是模型。
3、构建损失函数方程
很明显,这些参数构建起来的模型答案并不是唯一的。所以有必要建立一个标准,大概什么样的参数才是最合适的。所以,为了做到这一点,有必要构建一个损失函数。这个损失函数可以很简单,比如说向量的平方差之和,sum = (Yi - Yi_pred)**2 | i = 1,...,n。当然,也可以构建其他的损失函数,自定义一个立方差之和也可以的,sum = (Yi - Yi_pred)**3 | i = 1,...,n。
实际计算的时候,之前构建的y=f(x)也会带入到损失函数里面,它可以是线性的,也可以是非线性的,和优化算法一起求解。
4、用梯度下降的方法去逼近结果
有了损失函数之后,下面就是寻找并且优化参数的过程。我们一般默认,最终会存在这样的一组参数,可以使得损失函数无限接近于0。那么怎么求解这些参数呢。通常用的比较多的方法,就是梯度下降的方法,去尽可能逼近这些参数,当然也可能是陷阱,比如局部最小化了。默认参数都是0,每次通过求解偏微分的方法,去逼近这些参数数据。
θ:=θ−η⋅∇θL(θ)
这里面的θ就是参数,η是学习率,∇θL(θ)是损失函数下对θ的偏微分求导。拿深度学习来说,里面不同参数的求导,其实就是一个链式求导的过程,即
dl/dw = dl/dy * dy/dz * dz/dw
所以,这里只需要对每一部分求导,最后串起来即可。链式求导+梯度下降算法,是深度学习bp训练的核心。
5、AI里面的预测和训练
所谓的预测,一般就是说model训练好了,也就是各个参数都ok了。这样输入一组数据,就可以直接解算,拿到结果了。而训练,则是根据输入数据、输出数据,构建一个方程、确定损失函数和优化方法之后,开始通过训练的方法获得一组参数。整个过程可能很长,也可能很短。训练的时候,我们可能得到好多参数,最终部署的时候,一般会选用那组泛化效果最好的参数集合。
for i = 0 to max_epoch_times:total_loss = 0for data in data_set:result = predict(data)loss = result - real_resulttotal_loss += losstraining(data, loss) # mainly calculate gradient decentupdate_param_using_gradient()reset_gradient()print_loss_and_accurarcy()6、可以用线性回归去理解深度学习和AI
除了子函数、子方程不一样之外,线性回归是非常适合用来理解深度学习和ai的。比如说,什么是激励方程、什么是损失函数、什么是优化算法,以及为什么要对数据进行清洗,为什么要对数据归一化处理,还有什么是梯度下降、什么是训练、什么是预测,什么时候又要停止训练,都可以从线性回归的代码中找到答案。
最后给出一个线性规划的case,用easyx实现的,大家可以看一下,我们是如果用损失函数和梯度下降、偏微分来解决训练的问题的。并且看看,最终得到的w和b是不是那么回事。
#include <graphics.h> // important header file#include <conio.h>
#include <iostream>
#include <vector>
#include <cmath>
#include <random>
#include <iomanip>
using namespace std;// generate data between 0 and 1static double rand_func()
{static std::mt19937 gen((unsigned int)time(NULL)); // using time(NULL) as seedstatic std::normal_distribution<double> dist(0.0, 1.0);return dist(gen);
};// function starts from hereint main(int argc, char* argv[])
{const int N = 100;const double X_MIN = 0.0;const double X_MAX = 10.0;std::vector<double> X(N);std::vector<double> y(N);// generate data with noise: y = 2x + 1 + randomfor (int i = 0; i < N; i++){X[i] = X_MIN + (X_MAX - X_MIN) * i / (N - 1);y[i] = 2.0 * X[i] + 1.0 + rand_func();}// initial parameterdouble w = 0.0;double b = 0.0;const double alpha = 0.01;const int epochs = 1000;// do trainingfor (int epoch = 0; epoch < epochs; ++epoch){double dw = 0.0;double db = 0.0;double loss = 0.0;// calculate gradientfor (int i = 0; i < N; ++i){double y_pred = w * X[i] + b; // basic knowledgedouble error = y_pred - y[i];loss += error * error;dw += 2 * error * X[i]; // need know principal of gradient firstdb += 2 * error;}// do average processing hereloss /= N;dw /= N;db /= N;// update w and b, just like deep learningw -= alpha * dw;b -= alpha * db;// print epoch log hereif (epoch % 100 == 0){std::cout << "Epoch " << std::setw(4) << epoch \<< " | Loss: " << std::fixed << std::setprecision(4) << loss \<< " | w: " << std::setprecision(3) << w \<< ", b: " << b << std::endl;}}// initial windowinitgraph(800, 600);setbkcolor(WHITE);cleardevice();setlinecolor(BLACK);// get y_min and y_maxdouble y_min = *std::min_element(y.begin(), y.end());double y_max = *std::max_element(y.begin(), y.end());// compare data, make sure all data could be seeny_min = (y_min < (w * X_MIN + b)) ? y_min : (w *X_MIN + b);y_max = (y_max > (w * X_MAX + b)) ? y_max : (w * X_MAX + b);// define function pointerauto to_screen_x = [&](double x) -> int{return static_cast<int>((x - X_MIN) / (X_MAX - X_MIN) * 700 + 50);};auto to_screen_y = [&](double y_val) -> int{return static_cast<int>(550 - (y_val - y_min) / (y_max - y_min) * 500);};// draw data - bluesetfillcolor(RGB(0, 0, 255));for (int i = 0; i < N; ++i){int sx = to_screen_x(X[i]); // get positionint sy = to_screen_y(y[i]);fillcircle(sx, sy, 3); // draw solid circle here}// draw line - redsetcolor(RGB(255, 0, 0));int x1 = to_screen_x(X_MIN); // get positionint y1 = to_screen_y(w * X_MIN + b);int x2 = to_screen_x(X_MAX);int y2 = to_screen_y(w * X_MAX + b);line(x1, y1, x2, y2); // draw line here// titlesettextcolor(BLACK);settextstyle(20, 0, _T("SimSun"));outtextxy(180, 20, _T("Result of Linear Regression (Gradient Descent)"));// wait for input and then exit_getch();closegraph();return 0;
}注:
也可以用python实现,其实明白了原理之后,用什么原理实现其实不太那么重要。
# -*- coding: utf-8 -*-
"""
sample of Newton-GaussianC:\Python27\python.exe run.py
"""import numpy as np
import matplotlib.pyplot as plt
import time# generate noise datanp.random.seed(int(time.time()))
X = np.linspace(0, 10, 100)
y = 2 * X + 1 + np.random.randn(100)# init parameterw = 0.0  # weight, start from 0.0
b = 0.0  # bias, also start from 0.0alpha = 0.01  # learning rate
epochs = 1000  # defined iterate times# do training herefor i in range(epochs):# prediction herey_pred = w * X + berror = y_pred - y# computer loss here, also used to calculate gradientloss = np.mean((error) ** 2)# calculate gradient, very importantdw = np.mean(2 * (error) * X)db = np.mean(2 * (error))# update parameter according to dw and dbw -= alpha * dwb -= alpha * db# print log each 100 timesif i % 100 == 0:print("Epoch {:4d} | Loss: {:.4f} | w: {:.3f}, b: {:.3f}".format(i, loss, w, b))# draw final resultplt.scatter(X, y, label=u'Data')
plt.plot(X, w * X + b, color='red', label=u'Line') # draw line here
plt.legend()
plt.title(u"Result of Newton-Gaussian Algorithm")
plt.show()
