海思对于深度学习,提供了多层感知器的人工神经网络预测支持。其操作比较简单,加载由转换工具转换后的opencv训练产生的模型文件,组织测试数据,输入模型进行预测。
操作的时候,重点要注意的是,海思对于接收到的输入是按照s16q16的格式来解析的,所以,对于同样一个数值,给opencv训练,和给海思预测,就需要有不同的形式,以此,让它们两者从自己的角度来看,看到的是同一个数值。
参考http://blog.163.com/yql_bl/blog/static/847851692008112013117685/
参考http://blog.163.com/yql_bl/blog/static/847851692008112013117685/
HI_UxQyFz\HI_SxQy:
U 后面的数字 x 表示是用 x bit 无符号数据表示整数部分;
S 后面的数字 x 表示用 x bit 有符号数据表示整数部分;
Q 后面的数字 y 表示用 y bit 数据表示小数部分;
F 后面的数字 z 表示用 z bit 来表示标志位;
从左到右依次表示高 bit 位到低 bit 位。
如S16Q16表示,32bit的数据,左边(高)16bit表示的是整数数据,右边(低)16bit表示浮点数据。
那么对于一个二进制:0000000000000001 1010000000000000
就相当于是2^1+2^(-1)+2^(-3)=1.5625
同样的内存内容,按照不同的格式去解析的话,会得到不同的值。
我们基于这样的前提:标准格式表达的值,是真实的值。
将一个标准表示里的值,按照海思的表达方式重新表达。或者将海思形式的值,转换为标准的值。
这个就是正常的opencv的训练方式。
海思提供了模型转换工具,将opencv训练产生的xml模型文件转换为二进制的模型文件,供海思加载。
接口为:
HI_S32 HI_MPI_IVE_ANN_MLP_LoadModel(const HI_CHAR *pchFileName,
IVE_ANN_MLP_MODEL_S *pstAnnMlpModel)
【参数】
参数名称 |
描述 |
输入/输出 |
pchFileName |
模型文件路径及文件名。 不能为空。 |
输入 |
pstAnnMlpModel |
模型数据结构体指针。 不能为空。 |
输出 |
这里的内存都涉及到了地址对齐的要求,需要使用HI_MPI_SYS_MmzAlloc接口进行内存分配。
输入层的每一维都是一个SQ16.16的变量,32bit的长度。
输入层所需要的内存空间=(输入层的维数+1)*sizeof(SQ16.16)
地址对齐要求为16byte。
输出层的每一维都是一个SQ16.16的变量,32bit的长度。
输出层所需要的内存空间=(输出层的维数)*sizeof(SQ16.16)
地址对齐要求为16byte。
原始图片的数据读取后保存的内存。这个可以有,也可以没有,看数据来源,以及怎么处理数据来源,如模拟四象限分类的数据,完全可以手工模拟出来。
这个内存中存储的是原始的数据,需要经过与opencv训练时采取的一致的特征提取方式提取(组织)特征,然后将各特征值,赋值给输入层的内存。
对齐的要求,不少于16byte,高的取决于读取的图片的宽,计算出每一行的图片实际要分配多少空间,具体为:
u16Stride=(width+(16-width%16)%16)。
具体分配的内存空间大小,首先按照每一行需要的空间的大小,然后根据图片的类型来计算。如是8位单通道的,则可以直接计算:u16Stride* height;
如是420sp,则:u16Stride*height*3/2
如是422sp,则:u16Stride*height*2
如是16位但通道的,则:u16Stride*height*sizeof(HI_U16)
如是u8c3的,则:u16Stride*height*3
如是s32c1或u32c1的,则:u16Stride*height*sizeof(HI_U32)
如是s64c1或u64c1的,则:u16Stride*height*sizeof(HI_U64)
完全可以不需要这样分配,可以根据opencv的接口来读取图片,进行特征提取。关键还是在于把特征传给输入层的内存。
激活函数的计算是指数类型的,计算较为耗时,创建了查找表以后,可以查表得到结果,加快速度。
查找表,其数据均为 S1Q15 类型数
据,最多 4096 个;鉴于当前 ANN 中支持的 Identify、Sigmoid、Gaussian 激活函数为奇或偶函数,查找表仅对输入 ∈ u [0, pstActivFuncTab→s32TabInUpper]建表及查表;对 ANN 激活函数建立查找表时用于归一化的 u8TabOutNorm 是表示移位的数目。
【定义】
typedef struct hiIVE_LOOK_UP_TABLE_S
{
IVE_MEM_INFO_S stTable;
HI_U16 u16ElemNum; /*LUT's elements number*/
HI_U8 u8TabInPreci;
HI_U8 u8TabOutNorm;
HI_S32 s32TabInLower; /*LUT's original input lower limit*/
HI_S32 s32TabInUpper; /*LUT's original input upper limit*/
}IVE_LOOK_UP_TABLE_S;
成员名称 |
描述 |
stTable |
查找表建立后的数据内存块信息。 |
u32ElemNum |
查找表元素个数。 |
s32TabInLower |
建立查找表的数值范围的下限。 |
s32TabInUpper |
建立查找表的数值范围的上限。 |
u8TabInPreci |
建立查找表的精度,(s32TabInUpper - s32TabInLower)/(1< |
u8TabOutNorm |
表示建立查找表时为将原始数据归一化进行移位的位数或者进行除法的除数 |
目前对于这个u8TabOutNorm还未弄清楚其使用机制。
示例代码为:
static HI_S32 SAMPLE_IVE_Ann_Mlp_CreateTable(IVE_LOOK_UP_TABLE_S* pstTable, HI_FLOAT fAlpha, HI_FLOAT fBeta)
{
HI_U32 i;
HI_S1Q15* ps1q15Tmp;
HI_FLOAT fExpIn;
HI_DOUBLE dExpOut;
//Check table size
if (pstTable->stTable.u32Size < pstTable->u16ElemNum * sizeof(HI_S1Q15))
{
SAMPLE_PRT("Invalid table size\n");
return HI_FAILURE;
}
ps1q15Tmp = (HI_S1Q15*)pstTable->stTable.pu8VirAddr;
for (i = 0; i < pstTable->u16ElemNum; i++)
{
fExpIn = (HI_FLOAT)i / (1 << pstTable->u8TabInPreci);
dExpOut = (2 / (1 + exp(-fAlpha * fExpIn)) - 1) * fBeta * (1 << 15) / (1 << pstTable->u8TabOutNorm);
ps1q15Tmp[i] = (HI_CLIP(SAMPLE_IVE_Round(dExpOut), (1 << 15) - 1, -(1 << 15)));
}
return HI_SUCCESS;
}
调用过程为:
pstAnnInfo->stTable.s32TabInLower = 0;
pstAnnInfo->stTable.s32TabInUpper = 1;//1;
pstAnnInfo->stTable.u8TabInPreci = 8;//12;
pstAnnInfo->stTable.u8TabOutNorm = 2;//2
pstAnnInfo->stTable.u16ElemNum=(pstAnnInfo->stTable.s32TabInUpper-pstAnnInfo->stTable.s32tabInLower)<
u32Size = pstAnnInfo->stTable.u16ElemNum * sizeof(HI_U16);
s32Ret = SAMPLE_COMM_IVE_CreateMemInfo(&(pstAnnInfo->stTable.stTable), u32Size);
特征提取的方式根据识别的要求而自定义,如人脸识别的特征提取与字符识别的特征提取方式不同。
不管用什么样的方式进行特征提取,都要保证:以相同的方式提取出来给opencv训练,给海思预测。
一种字符识别的特征提取方式描述如下:
测试(训练)图片是一张不是0就是255组成的黑白图片,没有其他灰度值。将这个图片按照n*n(如4*4)的方式划分,每个n*n的小图片累加其灰度值,然后进行简单的归一化。这里要注意了:因为海思看待输入的方式是不同的,是以s16q16的方式来看待输入的,所以,一个同样的数值给opencv的时候,可以按照标准的方式给,但是给海思的时候,就必须要转换成s16q16的表达方式了。(或者反过来也行,以s16q16的方式去解析当前的数值,给opencv,总之就是要让两者看到的数值是一样的)。
如0.5,按照标准的float的表达的话,其二进制方式为:
0 01111110 000000000000000000000000
具体float的表达方式参考相关资料。
但是要是以s16q16的表达方式来说,其二进制,就是这样的:
0000000000000000 1000000000000000
所以,在给海思的时候,给的是下面的这个二进制值,这个二进制值对应的int值就是32768,所以,给海思的时候,给的32768!
转换某个数值为s16q16的方法,封装了一个changeFloatToS16Q16函数来进行,具体在后面的“辅助工具与方法”中。
对于正数来说,其乘于65536得到的数值的二进制表示,正好是该正数的s16q16的表达方式,所以可以看到海思预测时候的例子用的是直接乘于65536.
具体为:
static HI_VOID SAMPLE_IVE_Ann_Mlp_BinFeature_For_Predict(HI_U8* pu8GrayImg, HI_U16 u16Width, HI_U16 u16Height, HI_S16Q16* ps16q16BinFeature)
{
HI_U32 u32Step = 16;//4;
HI_U32 u16Sum = 0;
HI_U16 i, j;
HI_U16 m, n;
HI_U16 u16FeatureNum = 0;
printf("calculate BinFeature:\n");
for (i = 0; i < u16Height - u32Step + 1; i += u32Step)
{
for (j = 0; j < u16Width - u32Step + 1; j += u32Step)
{
u16Sum = 0;
for (m = i; m < i + u32Step; m++)
{
for (n = j; n < j + u32Step; n++)
{
u16Sum += pu8GrayImg[m * u16Width + n];
}
}
ps16q16BinFeature[u16FeatureNum++] = changeFloatToS16Q16(u16Sum*1.0/ (u32Step * u32Step * 255));
}
}
}
那对于训练时,给opencv训练的数值,就直接是:
ps16q16BinFeature[u16FeatureNum++] = u16Sum*1.0/ (u32Step * u32Step * 255);
执行预测的接口为:
【语法】
HI_S32 HI_MPI_IVE_ANN_MLP_Predict(IVE_HANDLE *pIveHandle,
IVE_SRC_MEM_INFO_S *pstSrc, IVE_LOOK_UP_TABLE_S *pstActivFuncTab,
IVE_ANN_MLP_MODEL_S *pstAnnMlpModel, IVE_DST_MEM_INFO_S *pstDst, HI_BOOL
bInstant);
参数名称 |
描述 |
输入/输出 |
pIveHandle |
handle 指针。不能为空. |
输出 |
pstSrc |
输入样本向量(特征向量)指针。不能为空。 |
输入 |
pstActivFuncTab |
用于激活函数计算的查找表信息指针。不能为空。 |
输入 |
pstAnnMlpModel |
模型数据结构体指针。不能为空。 |
输入 |
pstDst |
预测结果向量指针。不能为空。 |
输出 |
bInstant |
及时返回结果标志。 |
输入 |
参数名称 |
支持类型 |
地址对齐 |
向量维数 |
pstSrc |
一维 SQ16.16 向量,实际截断到 SQ8.16 计算 |
16 byte |
取值范围:1~256; 实际值:pstAnnMlpModel→ au16LayerCount[0] 注意内存至少需要 sizeof(SQ16.16) * ( pstAnnMlpModel→ au16LayerCount[0] + 1) |
pstDst |
一维 SQ16.16 向量 |
16 byte |
取值范围:1~256; 实际值:pstAnnMlpModel→ au16LayerCount[pstAnnMlpMod el→u8LayerNum -1] |
以上的参数是:
分配好的输入层空间,里面有以s16q16表达的值;
查找表的指针;
加载好的模型;
分配好的接收输出预测值的空间;
至于句柄(handle)与标志(bInstance)的解析为:
句柄(handle)
用户在调用算子创建任务时,系统会为每个任务分配一个 handle,用于标识不同的任务。
及时返回结果标志 bInstant
用户在创建某个任务后,希望及时得到该任务完成的信息,则需要在创建该任务时,将 bInstant 设置为 HI_TRUE。否则,如果用户不关心该任务是否完成,建议将 bInstant 设置为 HI_FALSE,这样可以与后续任务组链执行,减少中断次数,提升性能。
调用了该预测接口以后,根据返回的handle,需要进行query(需要循环),看任务的完成情况如何,进行相应的处理。如:
//创建任务
s32Ret = HI_MPI_IVE_ANN_MLP_Predict(&iveHandle,
&(pstAnnInfo->stSrc),
& (pstAnnInfo->stTable),
&(pstAnnInfo->stAnnModel),
&(pstAnnInfo->stDst),
bInstant);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("HI_MPI_IVE_ANN_MLP_Predict fail,Error(%#x)\n", s32Ret);
break;
}
//查询任务情况
s32Ret = HI_MPI_IVE_Query(iveHandle, &bFinish, bBlock);
while (HI_ERR_IVE_QUERY_TIMEOUT == s32Ret)
{
usleep(100);
s32Ret = HI_MPI_IVE_Query(iveHandle, &bFinish, bBlock);
}
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("HI_MPI_IVE_Query fail,Error(%#x)\n", s32Ret);
break;
}
//准备输出结果
u16LayerCount = pstAnnInfo->stAnnModel.au16LayerCount[pstAnnInfo->stAnnModel.u8LayerNum - 1];
for (k = 0; k < u16LayerCount; k++)
{
printf(" ps16q16Dst[%d]=%d,H16Q16=%f\n", k,ps16q16Dst[k],calculateS16Q16_c(ps16q16Dst[k]));
if (s16q16Response < ps16q16Dst[k])
{
s16q16Response = ps16q16Dst[k];
s32ResponseCls = k;
}
}
最后得到的s32ResponseCls 就是预测该测试样本所属的类别。
利用海思提供的ive_tool_xml2bin_ui.exe工具,在window下执行转换。
//得到一个short的二进制字符串
//返回字符串的长度
int decShortToBin(int dec,char *bstr){
int mod=0;
char tmpstr[64];
bzero(tmpstr,sizeof(tmpstr));
bzero(bstr,sizeof(bstr));
int i=0;
while(dec>0){
mod=dec%2;
dec/=2;
tmpstr[i]=mod+'0';
i++;
}
//cout<<"i="<
while(i tmpstr[i++]='0'; } //cout<<"tmpstr="< unsigned int len=strlen(tmpstr); for(i=0;i bstr[i]=tmpstr[len-i-1]; } return (int)len; } int decIntToBin(int dec,char *bstr){ int mod=0; char tmpstr[64]; bzero(tmpstr,sizeof(tmpstr)); bzero(bstr,sizeof(bstr)); int i=0; while(dec>0){ mod=dec%2; dec/=2; tmpstr[i]=mod+'0'; i++; } //cout<<"i="<
while(i tmpstr[i++]='0'; } //cout<<"tmpstr="< unsigned int len=strlen(tmpstr); for(i=0;i bstr[i]=tmpstr[len-i-1]; } return (int)len; } float calculateS16Q16_c(int value){ char bs[64]; decIntToBin(value,bs); //cout<<"calculateS16Q16_c:value="< float fout=0; //cout<<"calculateS16Q16_c----------begin"< int i=16; for(i=16;i<32;i++){//the higher 16bit standard for float value---------different from bitset if(bs[i]=='1'){ fout+=pow(2,(i-16+1)*(-1)); //cout<<"+2^("<<(i-16+1)*(-1)<<")"; } } //cout<<"\ncalculateS16Q16_c result="< for(i=0;i<16;i++){ if(bs[i]=='1'){ fout+=pow(2,(16-i-1)); } } return fout; } 将一个float值,表示为以s16q16表示的形式,返回的是s16q16的形式的内存,按照正常的理解所代表的值。 int changeFloatToS16Q16(float val){ int sign=1; if(val<0){ sign=-1; } int intPart=0; float floatPart=0; intPart=(int)abs(val); floatPart=(val>0?val:val*(-1))-intPart; //for float part bitset<16> fpbs; float tmpFl=floatPart; for(int i=15;i>=0;i--){ tmpFl=tmpFl*2; if(tmpFl>=1){ fpbs[i]=1; tmpFl-=1; }else{ fpbs[i]=0; } } int sPart=intPart*sign; bitset<16> ipbs(sPart); //out // // cout<<"float val="< bitset<32> resultBs; int result=0; for(int i=0;i<16;i++){//highest one is sign resultBs[32-16+i]=ipbs[i]; } for(int i=0;i<16;i++){ resultBs[i]=fpbs[i]; } // cout<<"resultBs="< result=resultBs.to_ulong(); return result; } 以y=kx直线进行划分,在直线以下的为类别0,其他为类别1。 在训练的时候,指定k,产生训练数据,同时将一部分作为测试数据。 /** * 以斜率=slope 来做分界,训练一个mlp模型 */ extern "C" void train_2_class_slope(float slope){//(int useExistModel,float from_x,float end_x,float from_y,float end_y,float x_step,float y_step){ CvANN_MLP annMlp; int outputClassCnt=2; bool loadModelFromFile=false; Mat training_datas; Mat trainClasses; Mat oriTrainDatas; generateFix2ClassSlopeTrainData(slope,training_datas,trainClasses); training_datas.convertTo(training_datas,CV_32FC1); trainClasses.convertTo(trainClasses,CV_32FC1); cout<<"training_datas=\n"< cout<<"trainClasses=\n"< //创建mlp Mat layers(1, 3, CV_32SC1); layers.at cout<<"------------------------trainAnnModel.input sample cnt:"< layers.at layers.at cout<<"outputClassCnt="< annMlp.create(layers, CvANN_MLP::SIGMOID_SYM, 0.6667f, 1.7159f); //--------训练mlp-----------// // Set up BPNetwork‘s parameters CvANN_MLP_TrainParams params; params.train_method = CvANN_MLP_TrainParams::BACKPROP; params.bp_dw_scale = 0.001; params.bp_moment_scale = 0.0; CvTermCriteria criteria; criteria.max_iter = 300; criteria.epsilon = 9.999999e-06; criteria.type = CV_TERMCRIT_ITER | CV_TERMCRIT_EPS; params.term_crit = criteria; annMlp.train(training_datas, trainClasses, Mat(), Mat(), params); cout<<"train finished"< char _dstPath[256]; sprintf(_dstPath,"data/my/my_simple_2_class_20160307_slope_%.2f.xml",slope); string dstPath(_dstPath);//="data/my/my_simple_2_class_20160307_slope_1.xml"; annMlp.save(dstPath.c_str()); cout<<"save model finished.model file="< //预测 Mat test_datas; Mat testClasses; int testCount=1;//每个象限的测试图片数量 Mat oriTestData; generate2ClassSlopeTestData(slope,test_datas,testClasses,oriTestData);//,from_x,end_x,from_y,end_y); test_datas.convertTo(test_datas,CV_32FC1); testClasses.convertTo(testClasses,CV_32FC1); cout<<"test_datas=\n"< // cout<<"testClasses=\n"< int correctCount=0; int errorCount=0; cout<<"test_datas size="< int totalTestSize=test_datas.rows; bool right=false; // TestData_2Feature* cur=testDataHead; int expected_idx=0; for(int i=0;i Mat predict_result(1, outputClassCnt, CV_32FC1); annMlp.predict(test_datas.row(i), predict_result); Point maxLoc; double maxVal; minMaxLoc(predict_result, 0, &maxVal, 0, &maxLoc); right=false; if(test_datas.row(i).at expected_idx=0; }else{ expected_idx=1; } if(expected_idx==maxLoc.x){ ++correctCount; right=true; }else { ++errorCount; } cout<<"data:"< // cur=cur->next; } cout<<"total test data count="< } /** * y=x,划分, */ void generateFix2ClassSlopeTrainData(float slope,Mat& mat,Mat& labels){ vector vector float tmp1=0,tmp2=0; printf("generateFix2ClassSlopeTrainData begin\n"); int multi=1; float x_step=16; float y_step=16; int needTestSize=10; int nowTestSize=0; int loopcnt=0; ostringstream os; Int end_x=255; Int end_y=255; int getDataInterval=((end_x-0)/x_step * (end_y-0)/y_step)/needTestSize; printf("getDataInterval=%d,totalTrainSize=%d\n",(deltaX/x_step * deltaY/y_step)); for(int x=0;x for(int y=0;y ++loopcnt; dataVec.clear(); multi*=-1; tmp1=multi*(float)x;///255; dataVec.push_back(tmp1); tmp2=multi*(float)y;///255; dataVec.push_back(tmp2); // printf("tmp1=%f\n",tmp1); // Mat tpmat=Mat(dataVec).reshape(1,1).clone(); mat.push_back(Mat(dataVec).reshape(1,1).clone()); labVec.clear(); if(tmp1*slope>tmp2){// x> 为类0 labVec.push_back(1.0f); labVec.push_back(0.0f); labels.push_back(Mat(labVec).reshape(1,1).clone()); if(loopcnt%getDataInterval==0){ os<<"0:"; } }else{ labVec.push_back(0.0f); labVec.push_back(1.0f); labels.push_back(Mat(labVec).reshape(1,1).clone()); if(loopcnt%getDataInterval==0){ os<<"1:"; } } if(loopcnt%getDataInterval==0){ os< } } } //输出一部分作为测试文件 system("rm data/my/test2classdata_slope.list"); fstream ftxt; string testfile="data/my/test2classdata_slope.list"; ftxt.open(testfile.c_str(), ios::out | ios::app); if (ftxt.fail()) { cout << "创建文件:"< getchar(); } ftxt << os.str(); ftxt.close(); } /** * 测a试?y=kx的?分?类え?情é况? */ HI_VOID SAMPLE_IVE_Ann_predict_2class_slope(float slope){ // HI_CHAR* pchBinFileName; int height,width,image_type; char pchBinFileName[256]; sprintf(pchBinFileName,"./data/my/my_simple_2_class_20160307_slope_%.2f.bin",slope); // pchBinFileName = "./data/my/my_simple_2_class_20160307_slope_3.00.bin"; height=1; width=2; image_type=IVE_IMAGE_TYPE_S32C1; HI_S32 s32Ret; SAMPLE_IVE_ANN_INFO_S stAnnInfo; printf("use model bin file:%s\n",pchBinFileName); SAMPLE_COMM_IVE_CheckIveMpiInit(); s32Ret=SAMPLE_IVE_Ann_Mlp_2Class_Slope_Init(&stAnnInfo, pchBinFileName,image_type,height,width); if (HI_SUCCESS != s32Ret) { SAMPLE_PRT("SAMPLE_IVE_Ann_Mlp__2Class_Init fail\n"); goto ANN_FAIL; } // predict2ClassData(&stAnnInfo,slope); predict2ClassSlopeData(&stAnnInfo,slope); //uninit SAMPLE_IVE_Ann_Mlp_Uninit(&stAnnInfo); ANN_FAIL: SAMPLE_COMM_IVE_IveMpiExit(); } /****************************************************************************** * function : Ann mlp init ******************************************************************************/ static HI_S32 SAMPLE_IVE_Ann_Mlp_2Class_Slope_Init(SAMPLE_IVE_ANN_INFO_S* pstAnnInfo, HI_CHAR* pchBinFileName,int image_type,int height,int width ) { SAMPLE_PRT("SAMPLE_IVE_Ann_Mlp_Init.....\n"); HI_S32 s32Ret = HI_SUCCESS; HI_U32 u32Size; memset(pstAnnInfo, 0, sizeof(SAMPLE_IVE_ANN_INFO_S)); /** * 查é找ò表括?里?的?数簓值μ范?围§是?[0,1],?精?度è是?8位?,?即′1<<8=256,? * 表括?示?要癮被?分?成é256段?。£ * * */ pstAnnInfo->stTable.s32TabInLower = 0; pstAnnInfo->stTable.s32TabInUpper = 1;//1; pstAnnInfo->stTable.u8TabInPreci = 8;//12; pstAnnInfo->stTable.u8TabOutNorm = 2;//2 pstAnnInfo->stTable.u16ElemNum = (pstAnnInfo->stTable.s32TabInUpper-pstAnnInfo->stTable.s32TabInLower) << pstAnnInfo->stTable.u8TabInPreci; u32Size = pstAnnInfo->stTable.u16ElemNum * sizeof(HI_U16); // SAMPLE_PRT("stTable.s32TabInLower=%d,s32TabInUpper=%d,u8TabInPreci=%d,u8TabOutNorm=%d,u16ElemNum=%d\n",pstAnnInfo->stTable.s32TabInLower,pstAnnInfo->stTable.s32TabInUpper,pstAnnInfo->stTable.u8TabInPreci,pstAnnInfo->stTable.u8TabOutNorm,pstAnnInfo->stTable.u16ElemNum); s32Ret = SAMPLE_COMM_IVE_CreateMemInfo(&(pstAnnInfo->stTable.stTable), u32Size); if (s32Ret != HI_SUCCESS) { SAMPLE_PRT("SAMPLE_COMM_IVE_CreateMemInfo fail\n"); goto ANN_INIT_FAIL; } s32Ret = SAMPLE_IVE_Ann_Mlp_CreateTable(&(pstAnnInfo->stTable), 0.6667f, 1.7159f); // s32Ret = SAMPLE_IVE_Ann_Mlp_CreateTable(&(pstAnnInfo->stTable), 1.0f, 1.0f); if (s32Ret != HI_SUCCESS) { SAMPLE_PRT("SAMPLE_IVE_Ann_Mlp_CreateTable fail\n"); goto ANN_INIT_FAIL; } SAMPLE_PRT("begin to load model:%s\n",pchBinFileName); s32Ret = HI_MPI_IVE_ANN_MLP_LoadModel(pchBinFileName, &(pstAnnInfo->stAnnModel)); if (s32Ret != HI_SUCCESS) { SAMPLE_PRT("HI_MPI_IVE_ANN_MLP_LoadModel fail,Error(%#x)\n", s32Ret); goto ANN_INIT_FAIL; } printf("finish load model:%s\n",pchBinFileName); u32Size = pstAnnInfo->stAnnModel.au16LayerCount[0] * sizeof(HI_S16Q16);//输?入?层?需è要癮的?空?间?大洙?小?:阰输?入?层?的?元a素?个?数簓*每?个?元a素?的?大洙?小? printf("allocate memory for input,size=%d\n",u32Size); s32Ret = SAMPLE_COMM_IVE_CreateMemInfo(&(pstAnnInfo->stSrc), u32Size); if (s32Ret != HI_SUCCESS) { SAMPLE_PRT("SAMPLE_COMM_IVE_CreateMemInfo fail\n"); goto ANN_INIT_FAIL; } u32Size = pstAnnInfo->stAnnModel.au16LayerCount[pstAnnInfo->stAnnModel.u8LayerNum - 1] * sizeof(HI_S16Q16);//输?出?类え?别纄信?息¢所ù需è空?间?的?大洙?小?:阰输?出?层?类え?别纄数簓*每?个?类え?别纄数簓值μ的?占?的?空?间? // SAMPLE_PRT("annModel output class cnt=%d\n",pstAnnInfo->stAnnModel.au16LayerCount[pstAnnInfo->stAnnModel.u8LayerNum - 1]); printf("allocate memory for output,size=%d\n",u32Size); s32Ret = SAMPLE_COMM_IVE_CreateMemInfo(&(pstAnnInfo->stDst), u32Size); if (s32Ret != HI_SUCCESS) { SAMPLE_PRT("SAMPLE_COMM_IVE_CreateMemInfo fail\n"); goto ANN_INIT_FAIL; } ANN_INIT_FAIL: // printf("s32Ret=%d,HI_SUCCESS=%d\n",s32Ret,HI_SUCCESS); if (HI_SUCCESS != s32Ret) { SAMPLE_IVE_Ann_Mlp_Uninit(pstAnnInfo); } return s32Ret; } /** * 预¤测ay=kx的?分?类え? */ void predict2ClassSlopeData(SAMPLE_IVE_ANN_INFO_S* pstAnnInfo,float slope){ char* contFile="data/my/test2classdata_slope_eq_1.list"; printf("try to get file info:%s\n",contFile); TestData_2Feature* head=get2FeatureData(contFile); printf("after read file:%s,head=%p\n",contFile,head); if(!head){ printf("fail to read contFile:%s\n",contFile); return; } // printStringNode(head,"1"); // printStringNode(head,"2"); HI_S32 i, k; HI_S32 s32Ret; HI_S32 s32ResponseCls; HI_U16 u16LayerCount; HI_S16Q16* ps16q16Dst; HI_S16Q16 s16q16Response; HI_BOOL bInstant = HI_TRUE; HI_BOOL bFinish; HI_BOOL bBlock = HI_TRUE; // HI_CHAR achFileName[IVE_FILE_NAME_LEN]; FILE* pFp = HI_NULL; IVE_HANDLE iveHandle; int xs[3]={-5,-4,3}; int ys[3]={99,-10,10}; srand(time(NULL)); int totalCount=0; int correctCount=0; TestData_2Feature* cur=head; int cnt=0; int expected_idx=0; while(cur!=NULL){ // printf("flag=%d,filePath=%s,filenName=%s -->\n ",cur->flag,cur->fileFullPath,cur->fileName); ps16q16Dst = (HI_S16Q16*)pstAnnInfo->stDst.pu8VirAddr; s16q16Response = 0; s32ResponseCls = -1; HI_S16Q16* stSrc=(HI_S16Q16*)pstAnnInfo->stSrc.pu8VirAddr; stSrc[0]=changeFloatToS16Q16(cur->x1);//转换为以s16q16表示的数据 stSrc[1]=changeFloatToS16Q16(cur->x2); s32Ret = HI_MPI_IVE_ANN_MLP_Predict(&iveHandle, &(pstAnnInfo->stSrc), \ & (pstAnnInfo->stTable), &(pstAnnInfo->stAnnModel), &(pstAnnInfo->stDst), bInstant); if (s32Ret != HI_SUCCESS) { SAMPLE_PRT("HI_MPI_IVE_ANN_MLP_Predict fail,Error(%#x)\n", s32Ret); break; } s32Ret = HI_MPI_IVE_Query(iveHandle, &bFinish, bBlock); while (HI_ERR_IVE_QUERY_TIMEOUT == s32Ret) { usleep(100); s32Ret = HI_MPI_IVE_Query(iveHandle, &bFinish, bBlock); } if (HI_SUCCESS != s32Ret) { SAMPLE_PRT("HI_MPI_IVE_Query fail,Error(%#x)\n", s32Ret); break; } u16LayerCount = pstAnnInfo->stAnnModel.au16LayerCount[pstAnnInfo->stAnnModel.u8LayerNum - 1]; // SAMPLE_PRT("pstAnnInfo->CstAnnModel.u8LayerNum=%d,pstAnnInfo->stAnnModel.au16LayerCount[pstAnnInfo->stAnnModel.u8LayerNum - 1]=%d\n",pstAnnInfo->stAnnModel.u8LayerNum,pstAnnInfo->stAnnModel.au16LayerCount[pstAnnInfo->stAnnModel.u8LayerNum - 1]); SAMPLE_PRT(" \n--predict2ClassSlopeData--Begin-- x1=%f(s16q16=%d),x2=%f(s16q16=%d)\n",cur->x1,changeFloatToS16Q16(cur->x1),cur->x2,changeFloatToS16Q16(cur->x2)); ++totalCount; for (k = 0; k < u16LayerCount; k++) { printf(" ps16q16Dst[%d]=%d,H16Q16=%f\n", k,ps16q16Dst[k],calculateS16Q16_c(ps16q16Dst[k])); if (s16q16Response < ps16q16Dst[k]) { s16q16Response = ps16q16Dst[k]; s32ResponseCls = k; } } if(cur->x1*slope>cur->x2){ expected_idx=0; }else{ expected_idx=1; } SAMPLE_PRT(" --predict2ClassSlopeData--End-- result:%s,flag:%d,class:%d ------\n\n",(expected_idx==s32ResponseCls?"right":"wrong"),expected_idx,s32ResponseCls); cur=cur->next; } freeTestData_2FeatureNode(head); } 附上斜率为的预测结果输出: [predict2ClassSlopeData]-830: --predict2ClassSlopeData--Begin-- x1=0.220000(s16q16=14417),x2=0.100000(s16q16=6553) ps16q16Dst[0]=46174,H16Q16=0.704559 ps16q16Dst[1]=20098,H16Q16=0.306671 [predict2ClassSlopeData]-847: --predict2ClassSlopeData--End-- result:right,flag:0,class:0 ------ [predict2ClassSlopeData]-830: --predict2ClassSlopeData--Begin-- x1=-1.000000(s16q16=-65536),x2=-3.000000(s16q16=-196608) ps16q16Dst[0]=48919,H16Q16=0.746445 ps16q16Dst[1]=15412,H16Q16=0.235168 [predict2ClassSlopeData]-847: --predict2ClassSlopeData--End-- result:right,flag:0,class:0 ------ [predict2ClassSlopeData]-830: --predict2ClassSlopeData--Begin-- x1=1.000000(s16q16=65536),x2=0.200000(s16q16=13107) ps16q16Dst[0]=48919,H16Q16=0.746445 ps16q16Dst[1]=15412,H16Q16=0.235168 [predict2ClassSlopeData]-847: --predict2ClassSlopeData--End-- result:right,flag:0,class:0 ------ [predict2ClassSlopeData]-830: --predict2ClassSlopeData--Begin-- x1=0.200000(s16q16=13107),x2=0.700000(s16q16=45875) ps16q16Dst[0]=16687,H16Q16=0.254623 ps16q16Dst[1]=51450,H16Q16=0.785065 [predict2ClassSlopeData]-847: --predict2ClassSlopeData--End-- result:right,flag:1,class:1 ------ [predict2ClassSlopeData]-830: --predict2ClassSlopeData--Begin-- x1=0.400000(s16q16=26214),x2=0.900000(s16q16=58982) ps16q16Dst[0]=16830,H16Q16=0.256805 ps16q16Dst[1]=51033,H16Q16=0.778702 [predict2ClassSlopeData]-847: --predict2ClassSlopeData--End-- result:right,flag:1,class:1 ------ [predict2ClassSlopeData]-830: --predict2ClassSlopeData--Begin-- x1=0.690196(s16q16=45232),x2=0.062745(s16q16=4112) ps16q16Dst[0]=48919,H16Q16=0.746445 ps16q16Dst[1]=15412,H16Q16=0.235168 [predict2ClassSlopeData]-847: --predict2ClassSlopeData--End-- result:right,flag:0,class:0 ------ [predict2ClassSlopeData]-830: --predict2ClassSlopeData--Begin-- x1=224.000000(s16q16=14680064),x2=80.000000(s16q16=5242880) ps16q16Dst[0]=17622,H16Q16=0.268890 ps16q16Dst[1]=45294,H16Q16=0.691132 [predict2ClassSlopeData]-847: --predict2ClassSlopeData--End-- result:wrong,flag:0,class:1 ------ [predict2ClassSlopeData]-830: --predict2ClassSlopeData--Begin-- x1=-224.000000(s16q16=-14680064),x2=80.000000(s16q16=5242880) ps16q16Dst[0]=17117,H16Q16=0.261185 ps16q16Dst[1]=51728,H16Q16=0.789307 [predict2ClassSlopeData]-847: --predict2ClassSlopeData--End-- result:right,flag:1,class:1 ------4.2.2 获得一个int数据的二进制字符串
4.2.3 解析一个以s16q16格式表示的内存,其真正的值是多少
4.2.4 将一个float值,转换为S16q16的值
5 完整示例
5.1 二维数据的训练与预测
5.1.1 训练二维数据
5.1.1.1 训练入口
5.1.1.2 训练与测试数据产生方法
5.1.2 海思预测二维数据样本的所属类别
5.1.2.1 预测入口
5.1.2.2 初始化
5.1.2.3 预测