精华内容
下载资源
问答
  • huaweicloud_garbage_classify nets 包含vgg16,resnet50,senet50 metrics 包含各种softmax的改进,时间原因无法把前三个用于分类;NormFace可以应对类别数据不平衡,不过目前效果和softmax差不多。 others 基于...
  • KNN classify

    2018-10-16 08:56:28
    KNN 分类器的经典算法 MATLAB代码,测试通过,可采用不同的距离计算
  • Android_TF_Classify

    2018-04-25 15:39:24
    用机器学习TensorFlow生成自己的图像模型,快速实现Android图像识别应用,
  • textclassify 利用bow(词袋特征)、tfidf、word2vec进行中文文本分类 下图为部分数据集 第一列为分类标签,第二列为文本数据,是关于七类文学作品的简介 requirements gensim sklearn bow accuracy=0.918533,...
  • classify-app-源码

    2021-05-11 05:24:43
    杜威分类应用程序客户端 一个虚拟书架,您可以在其中添加和分类书。 链接到实时应用 该应用程序的实时版本可在此处找到 概括 杜威分类应用程序使用户成为他们自己图书馆的图书馆员。 用户可以使用简化的Dewey十进制...
  • caffe-classify工程

    2019-01-08 14:41:59
    此资源为caffe的classify工程,VS2013的工程,无需进行任何配置,此版本为CPU版本,无需依赖其他任何库,本工程已经包含所需要的依赖库,
  • pytorch_vgg16_classify.py

    2020-09-07 21:14:28
    pytorch1.5实现的vgg16分类。在真实数据集测试成功 pytorch1.5实现的vgg16分类。在真实数据集测试成功 pytorch1.5实现的vgg16分类。在真实数据集测试成功
  • 猫狗分类 CNN模型,用于用猫或狗对图像进行分类。 用5个“ relu”层和一个“ Sigmoid”层对CNN模型进行训练,以对给定图像包含猫还是狗进行分类。 该模型仅训练了20个纪元,因此准确率达到了75%。...
  • image classify

    2019-01-17 11:48:56
    图像分类、场景分类;使用resnet网络;tensorflow;python代码
  • 前言 本文介绍的分类方式可能比较繁琐,因为它是采用华为云比赛的提交模式进行的。...1.图像分类的更多tricks(注意力机制 keras,TensorFlow和pytorch 版本等): 2.大家如果对目标检测比赛比较感兴趣的话,可以看一下...
  • kaggle-plant_classify-源码

    2021-03-10 20:00:43
    kaggle-plant_classify
  • 分类.js 用于经典对象继承的 JavaScript 库
  • Classify.py

    2020-03-15 09:45:39
    from sklearn.neighbors import KNeighborsClassifier X = [[0], [1], [2], [3], [4], [5], [6], [7], [8]]#data y = [0, 0, 0, 1, 1, 1, 2, 2, 2]#target neigh = KNeighborsClassifier(n_neighbors=3) ...
  • caffe classify 修改文件

    2016-10-13 17:15:36
    对caffe框架的python/classify.py文件进行修改,解决需要输入mean文件,更好地可视化输出分类信息,方便传输label文件。
  • VPP classify ACL

    千次阅读 2019-09-23 10:34:35
    本文对VPP 的classify ACL的使用做一些简单说明: 1、配置流程 1> 配置一个classify table vpp cmd: classify table [miss-next|l2-miss_next|acl-miss-next <next_index>] mask <mask-value> ...

    本文对VPP 的classify ACL的使用做一些简单说明:

    1、配置流程

    1> 配置一个classify table

    vpp cmd: 

    classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]
                        mask <mask-value> buckets <nn> [skip <n>] [match <n>]
                        [current-data-flag <n>] [current-data-offset <n>] [table <n>]
                        [memory-size <nn>[M][G]] [next-table <n>]
                        [del] [del-chain]

    2> 向table中添加控制session

    classify session [hit-next|l2-input-hit-next|l2-output-hit-next|acl-hit-next <next_index>|policer-hit-next <policer_name>]
                        table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]
                        [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]

    3> 在接口上启用 ACL 表

    set interface input acl intfc <int> [ip4-table <index>]  [ip6-table <index>] [l2-table <index>] [del]

    set interface output acl intfc <int> [ip4-table <index>]  [ip6-table <index>] [l2-table <index>] [del]

    2、classify table命令说明

    classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]
                        mask <mask-value> buckets <nn> [skip <n>] [match <n>]
                        [current-data-flag <n>] [current-data-offset <n>] [table <n>]
                        [memory-size <nn>[M][G]] [next-table <n>]
                        [del] [del-chain]

    查看table:show classify tables [index <nn>]

    在这些配置中主要的是mask;

    首先来看一个配置用例:

    classify table mask l3 ip4  version src dst proto 

    该配置表明该表匹配的是ip头的对应字段包括:版本号、源ip\目的ip\协议;

    在来看看该配置对应的mask的数据:

        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xF0 0x00

        0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF

        0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 

    部分参数说明:

    [miss-next|l2-miss_next|acl-miss-next <next_index>]:

    mask <mask-value>:设置该表是用数据包中的哪些字段用于过滤;(mask是以16个字节为一组的数据)

    下面列了一部分mask的配置命令:

         mask hex 112233445566

         mask l2 [dst] [src]  [proto]
                0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00
                mask l3 [ip4|ip6]  [version] [src] [dst] [proto] [hdr_length] [tos] [length] [fragment_id] [ttl] [protocol] [checksum]

         mask l4 [ tcp [src | dst]  | udp [src_port | dst_port]  | src_port  | dst_port ]

    buckets <nn>:buckets的最大数目

    skip <n>:mask中跳过的全0数据的组数

    match <n>:mask中有效组数

    current-data-flag <n>:标识数据包过滤的头信息获取是从vbuff的curretn_data处加current-data-offset 获取

    current-data-offset <n>:与current-data-flag配合使用

    table <n>:表索引;新增就不填,只有更新表信息才指定索引;

    [memory-size <nn>[M][G]]:classify table对应结构中mheap的大小

    3、classify session命令说明

    classify session [hit-next|l2-input-hit-next|l2-output-hit-next|acl-hit-next <next_index>|policer-hit-next <policer_name>]
                        table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]
                        [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]

    [hit-next|l2-input-hit-next|l2-output-hit-next|acl-hit-next <next_index>|policer-hit-next <policer_name>]:指定匹配成功后的操作;

    例如:acl-hit-next deny 匹配成功丢弃

               acl-hit-next ip4-node lh-test 匹配成功将下一条的节点设置为lh-test

    table-index <nn>:指定该session所属表索引

    match [hex] [l2] [l3 ip4] :指定上面classify table中mask对应字段的具体值;

    例如:match l3 ip4 src 172.16.101.53 匹配ip头的源地址为172.16.101.53的包;

     

    4、vpp中的流程
     

    1> classify table

    创建如果未指定table <n>就认为是新增:会在vnet_classify_main.tables

    typedef struct vnet_classify_table_t
    {
      u32x4 *mask;/*在跳过N个向量后应用掩码*/
      vnet_classify_bucket_t *buckets;
      vnet_classify_entry_t *entries;//classify session的结构
      /* 配置参数 */
      u32 match_n_vectors;//mask以u32x4(16字节)字节对齐mask的组数
      u32 skip_n_vectors;//跳过的u32x4个数
      u32 nbuckets;//设置buckets中有效的向量索引
      u32 log2_nbuckets;
      u32 linear_buckets;
      int entries_per_page;
      u32 active_elements;
      u32 current_data_flag;/*解析数据包的头从设置的偏移处获取 current_data_offset*/
      int current_data_offset;
      u32 data_offset;
      u32 next_table_index; /* 下一张表的索引 try */
      u32 miss_next_index; /* 没有匹配上的数据包下一跳的索引 Miss next index, return if next_table_index = 0 */
      vnet_classify_entry_t **working_copies; /* Per-bucket working copies, one per thread */
      int *working_copy_lengths;
      vnet_classify_bucket_t saved_bucket;
      vnet_classify_entry_t **freelists; /* Free entry freelists */
      u8 *name;
      void *mheap; /* Private allocation arena, protected by the writer lock */
      volatile u32 *writer_lock; /* Writer (only) lock for this table */
    } vnet_classify_table_t;
    

     

    2> classify session

    通过 table-index <nn>指定的索引到vnet_classify_main.tables中获取对应的ACL表;

    确定对应的hash值找到对应的bucket:

    hash = vnet_classify_hash_packet (t, key_minus_skip);//key_minus_skip就是数据包的头

    bucket_index = hash & (t->nbuckets - 1);

    b = &t->buckets[bucket_index];

    后面在数据包通过ip4-inacl这个节点进行处理的时候也是这样进行匹配到对应的session的

     

     

     

     

     


     

    展开全文
  • caffe classify文件

    2016-05-07 21:43:42
    caffe的classify.py修改版,支持直接输出结果
  • classify.py

    2017-07-01 21:26:47
    深度学习之神经网络框架Caffe的分类器。基于Caffe改进,使得结果阅读更容易。
  • classify->classify output->summary 写在开头(重复的) 1.课程来源:B站视频. 2.笔记目的:课程老师推荐个人学习+增强记忆+方便回顾 3.时间:2021年4月26日 4.同类笔记链接:(钩子:会逐渐增加20211001) 5...

    学习笔记:weka->classify->classify output->summary

    写在开头(重复的)

    1.课程来源:B站视频.
    2.笔记目的:课程老师推荐个人学习+增强记忆+方便回顾
    3.时间:2021年4月26日
    4.同类笔记链接:(钩子:会逐渐增加20211001)

    5.请一定观看视频课程,笔记是对视频内容的有限度的重现和基于个人的深化理解。
    6.注意符号 SS:意味着我的个人理解,非单纯授课内容,有可能有误哦。

    —以下正文—

    一、summary的简介

    • 1.关注在软件的哪里?是做什么的?在软件中的展现形式是什么样的?等基本问题

    (一)在哪里?

    • 1.在weka explorer页面的 classify选项卡 classifier output内部(注意调整滑块位置)
    • 请添加图片描述

    (二)做什么用的?

    • 1.经过载入数据集、选择classifier、选择测试集的获得方式、start,在classifier output中展示本次分类器给出的分类模型(J48给的树状,但不是这里介绍的重点)。通过模型在测试集上的工作效果,得到summary,既对模型的基于各种指标的评价
    • 2.必须说明的是,由于我还没有广泛的应用各种classifier,不知道每个classifier的summary是否一样。
    • 3.以下是weka自带的classifier展示
      请添加图片描述

    ------------下面开始逐项介绍summary里的各项内容-----------------

    二、Correctly Classified Instances(正确分类的实例)

    • 1.Correctly Classified Instances 779 (准确个数) 96.1728 %(准确率)
    • 2.这看起来是个不错的分类模型,不是么?(不引起歧义的,我称呼classifier为分类器,有分类器训练出来的,成为分类模型)

    三、Incorrectly Classified Instances(不正确分类的实例)

    • 1.Incorrectly Classified Instances 31 (不准确个数) 3.8272 %(不准确率)

    四、Kappa statistic(Kappa值)

    (一)什么是Kappa值

    • 1.Kappa值用于一致性检验,也可以用于衡量分类精度

    • 2.当Kappa值用于衡量分类精度时的计算方式如下(来自百度百科):
      在这里插入图片描述

      • 2.1 其中,po是每一类正确分类的样本数量之和除以总样本数,也就是总体分类精度 。
      • 2.2 假设每一类真实样本个数分别为a1,a2,…,aC,而预测出来的每一类的样本个数分别为b1,b2,…,bC总样本个数为n,则有:
      • 在这里插入图片描述
      • 2.3 kappa计算结果为-1-1,但通常kappa是落在 0-1 间,可分为五组来表示不同级别的一致性:0.0-0.20极低的一致性(slight)、0.21-0.40一般的一致性(fair)、0.41-0.60 中等的一致性(moderate)、0.61-0.80 高度的一致性(substantial)和0.81-1几乎完全一致(almost perfect)。
      • 2.4一个计算例子:
      • 在这里插入图片描述
      • 请添加图片描述
    • 3.当用于一致性检测时的计算方式如下(来自http://sofasofa.io/forum_main_post.php?postid=1000321):

      • 3.1 κappa值是如何计算的?我们直接上栗子。假设我们有两个对象,男生甲和女生乙,相亲。媒婆想知道他们两个能不能处得来,首先就想问能不能吃到一块去。就分别问两个人二十道菜,他们只需回答“喜欢”或者“不喜欢”这道菜。媒婆非常认真,做了下面的表,
        在这里插入图片描述

      • a是男生喜欢、女生也喜欢吃的菜的数量,b是男生喜欢、女生不喜欢的菜的数量,以此类推。

      • Kappa值的计算公式如下

      • 请添加图片描述

      • κ的值在−1到1之间。越接近1,两者越一致、越吻合。换句话说,男生甲和女生乙是有缘人!我们喜欢一样的东西,也讨厌一样的东西。接近0,表面两者之间符合偶然的预期。换句话说,男生甲和女生乙是路人!接近-1,表面两者之间的相符程度非常低。换句话说,男生甲和女生乙是冤家,死对头!我喜欢你讨厌的东西,我讨厌你喜欢的东西。

      • 3.2 具体来算两个例子。

        • 3.2.1例子一请添加图片描述

        • 说明这两人在饮食口味上有点相反,符合程度很低。于是媒婆重新找了女生来相亲。

        • 3.2.2 例子二
          请添加图片描述

        • 这次一算Kappa系数,发现有0.3,虽然不是很高,但是说明口味还是比较接近的。

      • 3.3 例子讲完了,下面说一说应用。之前kappa系数在医学领域应用比较多,比如利用症状的阴性和阳性诊断病情的一致性。现在在机器学习领域,也越来越多得被重视。Kappa系数可以用来评价一个分类器的准确性,特别是在标签不平衡的状态下。比如说:

      • 请添加图片描述

      • 如果采用一般的方法来评价这个分类器的话,我们发现它的精度到达了90%,看起来还不错。可实际并不是这样的。因为这个样本本身就很不平衡,95%的标签是“+”。计算一下,我们可以发现这个分类器的Kappa系数只有-0.05,说明这个预测结果不理想。

    (二)kappa值在分类模型的summary中的意义

    • 1.衡量分类的精度。由于J48是有监督的学习,每个预测结果都有观测结果与其对应。因此可以用kappa值衡量预测结果和观测结果的一致性——既衡量分类的精度。
    • 2.如果非要手算的话,用weka提供的混淆矩阵是方便的。真实值=每一行加起来,预测值=每一列加起来。然后按照上面的方法计算。

    五、Mean absolute error(平均绝对误差)与Root mean squared error(均方根误差)

    • 1.公式为:

    • 在这里插入图片描述

    • 在这里插入图片描述

    • 另外,标准差的公式为:

    • 在这里插入图片描述

    • 2.可以通过对三者两两对比加强记忆。

    • 2.1 标准差与Root mean squared error(均方根误差):区别在于,标准差衡量的是观测值和观测均值的差距,而RMSE衡量的是每一个预测值和其对应的一个观测值的差距。其次,从上述描述中可以看出来,两者的使用条件是不相同的。标准差只要求观测值,而均方根误差要求预测值和观测值
    • 2.2 Root mean squared error(均方根误差)和Mean absolute error(平均绝对误差):同样的,MAE和RMSE都解决了对正负误差相互抵消问题。但是,MAE显然是线性的——既每一个误差无论大小其在结果中的权重是一样的。而RMSE显然加重了对较大误差的惩罚

    六、Relative absolute error

    • 1.多方查找,从“From this presentation, in slide 22, and citing witten, here are the formulas:”找到如下公式,计算预测值和观测值的差的绝对值的和,计算观测值和观测平均值的差的绝对值的和,数一除以数二,得到relative absolute error。
      在这里插入图片描述
      -2. 此值越小实验约准确。

    七、Root relative squared error

    • 1.其公式为:但我觉得不对,应该开个根号。
    • 在这里插入图片描述

    八、Total Number of Instances(显然的,这是实例总数的意思)

    展开全文
  • Flow Classify示例应用程序基于转发应用程序的简单框架示例。 它旨在演示使用Flow Classify库API的DPDK转发应用程序的基本组件 flow_classify例子对于DPDK的学习具有很重要的意义,是比较重要的章节。有点类似于...

    前言

    Flow Classify示例应用程序基于转发应用程序的简单框架示例。
    它旨在演示使用Flow Classify库API的DPDK转发应用程序的基本组件

    flow_classify例子对于DPDK的学习具有很重要的意义,是比较重要的章节。有点类似于linux网络中的iptables功能,也有点类似于我们在linux内核中开发的防火墙功能。我们可以使用flow模块对数据包进行统计,丢弃等基本的操作。

    程序代码

    ACL介绍

    首先该例程中主要是面向的对象是IP流量中的五元组信息。即源ip地址,目的ip地址,源端口号,目的端口号,协议号。学过linux网络的都知道,该五元组可以决定一个数据包的唯一性。因为我们操作的是IP流量五元组,所以这里使用的ACL classify算法。ACL规则主要面向的是IP流量中的五元组信息。

    关于classify算法可以参考DPDK ACL算法介绍

    DPDK报文分类与访问控制

    dpdk提供了一个访问控制库,提供了基于一系列分类规则对接收到的报文进行分类的能力。

    ACL库用来在一系列规则上执行N元组查找,可以实现多个分类和对每个分类查找最佳匹配(最高优先级)。

    ACL库的api提供如下基本操作:

    • 创建一个新的访问控制(AC)环境实例(context)
    • 添加规则到这个环境实例
    • 为这个实例里所有的规则,创建必需的运行时结构体来指针报文分类
    • 执行接收报文分类
    • 删除AC环境实例和对应的运行时结构体,并释放内存

    该例程是对官方例程flow_classify补充说明flow_classify链接地址

    程序

    /* SPDX-License-Identifier: BSD-3-Clause
     * Copyright(c) 2017 Intel Corporation
     */
    
    #include <stdint.h>
    #include <inttypes.h>
    #include <getopt.h>
    
    #include <rte_eal.h>
    #include <rte_ethdev.h>
    #include <rte_cycles.h>
    #include <rte_lcore.h>
    #include <rte_mbuf.h>
    #include <rte_flow.h>
    #include <rte_flow_classify.h>
    #include <rte_table_acl.h>
    
    #define RX_RING_SIZE 1024
    #define TX_RING_SIZE 1024
    
    #define NUM_MBUFS 8191
    #define MBUF_CACHE_SIZE 250
    #define BURST_SIZE 32
    
    #define MAX_NUM_CLASSIFY 30
    #define FLOW_CLASSIFY_MAX_RULE_NUM 91
    #define FLOW_CLASSIFY_MAX_PRIORITY 8
    #define FLOW_CLASSIFIER_NAME_SIZE 64
    
    #define COMMENT_LEAD_CHAR	('#')
    #define OPTION_RULE_IPV4	"rule_ipv4"
    #define RTE_LOGTYPE_FLOW_CLASSIFY	RTE_LOGTYPE_USER3
    #define flow_classify_log(format, ...) \
    		RTE_LOG(ERR, FLOW_CLASSIFY, format, ##__VA_ARGS__)
    
    #define uint32_t_to_char(ip, a, b, c, d) do {\
    		*a = (unsigned char)(ip >> 24 & 0xff);\
    		*b = (unsigned char)(ip >> 16 & 0xff);\
    		*c = (unsigned char)(ip >> 8 & 0xff);\
    		*d = (unsigned char)(ip & 0xff);\
    	} while (0)
    
    enum {
    	CB_FLD_SRC_ADDR,
    	CB_FLD_DST_ADDR,
    	CB_FLD_SRC_PORT,
    	CB_FLD_SRC_PORT_DLM,
    	CB_FLD_SRC_PORT_MASK,
    	CB_FLD_DST_PORT,
    	CB_FLD_DST_PORT_DLM,
    	CB_FLD_DST_PORT_MASK,
    	CB_FLD_PROTO,
    	CB_FLD_PRIORITY,
    	CB_FLD_NUM,
    };
    
    static struct{
    	const char *rule_ipv4_name;
    } parm_config;
    const char cb_port_delim[] = ":";
    
    static const struct rte_eth_conf port_conf_default = {
    	.rxmode = {
    		.max_rx_pkt_len = ETHER_MAX_LEN,
    	},
    };
    
    struct flow_classifier {
    	struct rte_flow_classifier *cls;
    };
    
    struct flow_classifier_acl {
    	struct flow_classifier cls;
    } __rte_cache_aligned;
    
    /* ACL field definitions for IPv4 5 tuple rule */
    
    enum {
    	PROTO_FIELD_IPV4,
    	SRC_FIELD_IPV4,
    	DST_FIELD_IPV4,
    	SRCP_FIELD_IPV4,
    	DSTP_FIELD_IPV4,
    	NUM_FIELDS_IPV4
    };
    
    enum {
    	PROTO_INPUT_IPV4,
    	SRC_INPUT_IPV4,
    	DST_INPUT_IPV4,
    	SRCP_DESTP_INPUT_IPV4
    };
    
    static struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
    	/* first input field - always one byte long. */
    	{
    		.type = RTE_ACL_FIELD_TYPE_BITMASK,
    		.size = sizeof(uint8_t),
    		.field_index = PROTO_FIELD_IPV4,
    		.input_index = PROTO_INPUT_IPV4,
    		.offset = sizeof(struct ether_hdr) +
    			offsetof(struct ipv4_hdr, next_proto_id),
    	},
    	/* next input field (IPv4 source address) - 4 consecutive bytes. */
    	{
    		/* rte_flow uses a bit mask for IPv4 addresses */
    		.type = RTE_ACL_FIELD_TYPE_BITMASK,
    		.size = sizeof(uint32_t),
    		.field_index = SRC_FIELD_IPV4,
    		.input_index = SRC_INPUT_IPV4,
    		.offset = sizeof(struct ether_hdr) +
    			offsetof(struct ipv4_hdr, src_addr),
    	},
    	/* next input field (IPv4 destination address) - 4 consecutive bytes. */
    	{
    		/* rte_flow uses a bit mask for IPv4 addresses */
    		.type = RTE_ACL_FIELD_TYPE_BITMASK,
    		.size = sizeof(uint32_t),
    		.field_index = DST_FIELD_IPV4,
    		.input_index = DST_INPUT_IPV4,
    		.offset = sizeof(struct ether_hdr) +
    			offsetof(struct ipv4_hdr, dst_addr),
    	},
    	/*
    	 * Next 2 fields (src & dst ports) form 4 consecutive bytes.
    	 * 
    	 * They share the same input index.
    	 */
    	{
    		/* rte_flow uses a bit mask for protocol ports */
    		.type = RTE_ACL_FIELD_TYPE_BITMASK,
    		.size = sizeof(uint16_t),
    		.field_index = SRCP_FIELD_IPV4,
    		.input_index = SRCP_DESTP_INPUT_IPV4,
    		.offset = sizeof(struct ether_hdr) +
    			sizeof(struct ipv4_hdr) +
    			offsetof(struct tcp_hdr, src_port),
    	},
    	{
    		/* rte_flow uses a bit mask for protocol ports */
    		.type = RTE_ACL_FIELD_TYPE_BITMASK,
    		.size = sizeof(uint16_t),
    		.field_index = DSTP_FIELD_IPV4,
    		.input_index = SRCP_DESTP_INPUT_IPV4,
    		.offset = sizeof(struct ether_hdr) +
    			sizeof(struct ipv4_hdr) +
    			offsetof(struct tcp_hdr, dst_port),
    	},
    };
    
    /* flow classify data */
    static int num_classify_rules;
    static struct rte_flow_classify_rule *rules[MAX_NUM_CLASSIFY];
    static struct rte_flow_classify_ipv4_5tuple_stats ntuple_stats;
    static struct rte_flow_classify_stats classify_stats = {
    		.stats = (void **)&ntuple_stats
    };
    
    /* parameters for rte_flow_classify_validate and
     * rte_flow_classify_table_entry_add functions
     */
    
    static struct rte_flow_item  eth_item = { RTE_FLOW_ITEM_TYPE_ETH,
    	0, 0, 0 };
    static struct rte_flow_item  end_item = { RTE_FLOW_ITEM_TYPE_END,
    	0, 0, 0 };
    
    /* sample actions:
     * "actions count / end"
     */
    struct rte_flow_query_count count = {
    	.reset = 1,
    	.hits_set = 1,
    	.bytes_set = 1,
    	.hits = 0,
    	.bytes = 0,
    };
    // 启用流量计数器
    static struct rte_flow_action count_action = { RTE_FLOW_ACTION_TYPE_COUNT,
    	&count};
    static struct rte_flow_action end_action = { RTE_FLOW_ACTION_TYPE_END, 0};
    // rte_flow_action 结构体数组(terminated by the END pattern item),表示流规则的动作,比如QUEUE, DROP, END等等
    // 这里的action有两个动作,分别是计数和结束
    static struct rte_flow_action actions[2];
    
    /* sample attributes */
    static struct rte_flow_attr attr;	// 代表的一条流规则属性
    
    /* flow_classify.c: * Based on DPDK skeleton forwarding example. */
    
    /*
     * Initializes a given port using global settings and with the RX buffers
     * coming from the mbuf_pool passed as a parameter.
     */
    static inline int
    port_init(uint8_t port, struct rte_mempool *mbuf_pool)
    {
    	struct rte_eth_conf port_conf = port_conf_default;
    	struct ether_addr addr;
    	const uint16_t rx_rings = 1, tx_rings = 1;
    	int retval;
    	uint16_t q;
    	struct rte_eth_dev_info dev_info;
    	struct rte_eth_txconf txconf;
    
    	if (!rte_eth_dev_is_valid_port(port))
    		return -1;
    
    	// 设置port的属性
    	rte_eth_dev_info_get(port, &dev_info);
    	if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
    		port_conf.txmode.offloads |=
    			DEV_TX_OFFLOAD_MBUF_FAST_FREE;
    
    	/* Configure the Ethernet device. */
    	// 设置网卡接收和发送队列的个数以及属性
    	retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
    	if (retval != 0)
    		return retval;
    
    	/* Allocate and set up 1 RX queue per Ethernet port. */
    	for (q = 0; q < rx_rings; q++) {
    		// 分配一个接收队列
    		retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
    				rte_eth_dev_socket_id(port), NULL, mbuf_pool);
    		if (retval < 0)
    			return retval;
    	}
    
    	txconf = dev_info.default_txconf;
    	txconf.offloads = port_conf.txmode.offloads;
    	/* Allocate and set up 1 TX queue per Ethernet port. */
    	for (q = 0; q < tx_rings; q++) {
    		// 分配和设置一个发送队列
    		retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
    				rte_eth_dev_socket_id(port), &txconf);
    		if (retval < 0)
    			return retval;
    	}
    
    	/* Start the Ethernet port. */
    	// 开启网卡转发
    	retval = rte_eth_dev_start(port);
    	if (retval < 0)
    		return retval;
    
    	/* Display the port MAC address. */
    	rte_eth_macaddr_get(port, &addr);
    	printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
    			   " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
    			port,
    			addr.addr_bytes[0], addr.addr_bytes[1],
    			addr.addr_bytes[2], addr.addr_bytes[3],
    			addr.addr_bytes[4], addr.addr_bytes[5]);
    
    	/* Enable RX in promiscuous mode for the Ethernet device. */
    	// 开始网卡的混杂模式
    	rte_eth_promiscuous_enable(port);
    
    	return 0;
    }
    
    /*
     * The lcore main. This is the main thread that does the work, reading from
     * an input port classifying the packets and writing to an output port.
     */
    static __attribute__((noreturn)) void
    lcore_main(struct flow_classifier *cls_app)
    {
    	uint16_t port;
    	int ret;
    	int i = 0;
    	/* 
    		从flow_classifier表中删除流分类规则
    		cls_app->cls: 流分类器句柄
    		rules[7]: 流分类规则
    	*/
    	ret = rte_flow_classify_table_entry_delete(cls_app->cls,
    			rules[7]);
    	if (ret)
    		printf("table_entry_delete failed [7] %d\n\n", ret);
    	else
    		printf("table_entry_delete succeeded [7]\n\n");
    
    	/*
    	 * Check that the port is on the same NUMA node as the polling thread
    	 * for best performance.
    	 */
    	RTE_ETH_FOREACH_DEV(port)
    		if (rte_eth_dev_socket_id(port) > 0 &&
    			rte_eth_dev_socket_id(port) != (int)rte_socket_id()) {
    			printf("\n\n");
    			printf("WARNING: port %u is on remote NUMA node\n",
    			       port);
    			printf("to polling thread.\n");
    			printf("Performance will not be optimal.\n");
    		}
    	printf("\nCore %u forwarding packets. ", rte_lcore_id());
    	printf("[Ctrl+C to quit]\n");
    
    	/* Run until the application is quit or killed. */
    	for (;;) {
    		/*
    		 * Receive packets on a port, classify them and forward them
    		 * on the paired port.
    		 * The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
    		 */
    		RTE_ETH_FOREACH_DEV(port) {
    			/* Get burst of RX packets, from first port of pair. */
    			struct rte_mbuf *bufs[BURST_SIZE];
    			// 接收数据报文
    			const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
    					bufs, BURST_SIZE);
    
    			if (unlikely(nb_rx == 0))
    				continue;
    
    			// 遍历rules
    			for (i = 0; i < MAX_NUM_CLASSIFY; i++) {
    				if (rules[i]) {
    					
    					/*
    						查看burst中是否有任何数据包与表中的一条流规则匹配
    						cls_app->cls: 流分类器句柄
    						bufs: 指向数据报文
    						nb_rx: 数据报文个数
    						rules: 流分类器规则
    						classify_stats: 流分类器统计
    					*/
    					ret = rte_flow_classifier_query(
    						cls_app->cls,
    						bufs, nb_rx, rules[i],
    						&classify_stats);
    					if (ret)
    						printf(
    							"rule [%d] query failed ret [%d]\n\n",
    							i, ret);
    					else {
    						printf(
    						"rule[%d] count=%"PRIu64"\n",
    						i, ntuple_stats.counter1);
    
    						printf("proto = %d\n",
    						ntuple_stats.ipv4_5tuple.proto);
    					}
    				}
    			}
    
    			/* Send burst of TX packets, to second port of pair. */
    			const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
    					bufs, nb_rx);
    
    			/* Free any unsent packets. */
    			if (unlikely(nb_tx < nb_rx)) {
    				uint16_t buf;
    
    				for (buf = nb_tx; buf < nb_rx; buf++)
    					rte_pktmbuf_free(bufs[buf]);
    			}
    		}
    	}
    }
    
    /*
     * Parse IPv4 5 tuple rules file, ipv4_rules_file.txt.
     * Expected format:
     * <src_ipv4_addr>'/'<masklen> <space> \
     * <dst_ipv4_addr>'/'<masklen> <space> \
     * <src_port> <space> ":" <src_port_mask> <space> \
     * <dst_port> <space> ":" <dst_port_mask> <space> \
     * <proto>'/'<proto_mask> <space> \
     * <priority>
     */
    
    static int
    get_cb_field(char **in, uint32_t *fd, int base, unsigned long lim,
    		char dlm)
    {
    	unsigned long val;
    	char *end;
    
    	errno = 0;
    	val = strtoul(*in, &end, base);
    	if (errno != 0 || end[0] != dlm || val > lim)
    		return -EINVAL;
    	*fd = (uint32_t)val;
    	*in = end + 1;
    	return 0;
    }
    
    static int
    parse_ipv4_net(char *in, uint32_t *addr, uint32_t *mask_len)
    {
    	uint32_t a, b, c, d, m;
    
    	if (get_cb_field(&in, &a, 0, UINT8_MAX, '.'))
    		return -EINVAL;
    	if (get_cb_field(&in, &b, 0, UINT8_MAX, '.'))
    		return -EINVAL;
    	if (get_cb_field(&in, &c, 0, UINT8_MAX, '.'))
    		return -EINVAL;
    	if (get_cb_field(&in, &d, 0, UINT8_MAX, '/'))
    		return -EINVAL;
    	if (get_cb_field(&in, &m, 0, sizeof(uint32_t) * CHAR_BIT, 0))
    		return -EINVAL;
    
    	addr[0] = IPv4(a, b, c, d);
    	mask_len[0] = m;
    	return 0;
    }
    
    static int
    parse_ipv4_5tuple_rule(char *str, struct rte_eth_ntuple_filter *ntuple_filter)
    {
    	int i, ret;
    	char *s, *sp, *in[CB_FLD_NUM];
    	static const char *dlm = " \t\n";
    	int dim = CB_FLD_NUM;
    	uint32_t temp;
    
    	// 解析传入的字符串,将结果存入in数组中
    	s = str;
    	for (i = 0; i != dim; i++, s = NULL) {
    		in[i] = strtok_r(s, dlm, &sp);
    		if (in[i] == NULL)
    			return -EINVAL;
    		printf("============ %s\n", in[i]);
    	}
    
    	// 解析源ip地址和子网掩码
    	ret = parse_ipv4_net(in[CB_FLD_SRC_ADDR],
    			&ntuple_filter->src_ip,
    			&ntuple_filter->src_ip_mask);
    	if (ret != 0) {
    		flow_classify_log("failed to read source address/mask: %s\n",
    			in[CB_FLD_SRC_ADDR]);
    		return ret;
    	}
    
    	// 解析目的ip地址和子网掩码
    	ret = parse_ipv4_net(in[CB_FLD_DST_ADDR],
    			&ntuple_filter->dst_ip,
    			&ntuple_filter->dst_ip_mask);
    	if (ret != 0) {
    		flow_classify_log("failed to read source address/mask: %s\n",
    			in[CB_FLD_DST_ADDR]);
    		return ret;
    	}
    
    	// 获取源端口号
    	if (get_cb_field(&in[CB_FLD_SRC_PORT], &temp, 0, UINT16_MAX, 0))
    		return -EINVAL;
    	ntuple_filter->src_port = (uint16_t)temp;
    
    	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
    			sizeof(cb_port_delim)) != 0)
    		return -EINVAL;
    
    	// 获取源端口掩码
    	if (get_cb_field(&in[CB_FLD_SRC_PORT_MASK], &temp, 0, UINT16_MAX, 0))
    		return -EINVAL;
    	ntuple_filter->src_port_mask = (uint16_t)temp;
    
    	// 获取目的端口号
    	if (get_cb_field(&in[CB_FLD_DST_PORT], &temp, 0, UINT16_MAX, 0))
    		return -EINVAL;
    	ntuple_filter->dst_port = (uint16_t)temp;
    
    	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
    			sizeof(cb_port_delim)) != 0)
    		return -EINVAL;
    
    	// 获取目的端口掩码
    	if (get_cb_field(&in[CB_FLD_DST_PORT_MASK], &temp, 0, UINT16_MAX, 0))
    		return -EINVAL;
    	ntuple_filter->dst_port_mask = (uint16_t)temp;
    
    	// 获取l4协议号
    	if (get_cb_field(&in[CB_FLD_PROTO], &temp, 0, UINT8_MAX, '/'))
    		return -EINVAL;
    	ntuple_filter->proto = (uint8_t)temp;
    
    	// 获取协议号掩码
    	if (get_cb_field(&in[CB_FLD_PROTO], &temp, 0, UINT8_MAX, 0))
    		return -EINVAL;
    	ntuple_filter->proto_mask = (uint8_t)temp;
    
    	// 获取优先级
    	if (get_cb_field(&in[CB_FLD_PRIORITY], &temp, 0, UINT16_MAX, 0))
    		return -EINVAL;
    	ntuple_filter->priority = (uint16_t)temp;
    	if (ntuple_filter->priority > FLOW_CLASSIFY_MAX_PRIORITY)
    		ret = -EINVAL;
    
    	return ret;
    }
    
    /* Bypass comment and empty lines */
    static inline int
    is_bypass_line(char *buff)
    {
    	int i = 0;
    
    	/* comment line */
    	if (buff[0] == COMMENT_LEAD_CHAR)
    		return 1;
    	/* empty line */
    	while (buff[i] != '\0') {
    		if (!isspace(buff[i]))
    			return 0;
    		i++;
    	}
    	return 1;
    }
    
    static uint32_t
    convert_depth_to_bitmask(uint32_t depth_val)
    {
    	uint32_t bitmask = 0;
    	int i, j;
    
    	for (i = depth_val, j = 0; i > 0; i--, j++)
    		bitmask |= (1 << (31 - j));
    	return bitmask;
    }
    
    static int
    add_classify_rule(struct rte_eth_ntuple_filter *ntuple_filter,
    		struct flow_classifier *cls_app)
    {
    	int ret = -1;
    	int key_found;
    	
        /* rte_flow_item: ACL 规则的详细内容。
        会从最低协议层开始堆叠flow_item来形成一个匹配模式。必须由 end_item 结尾。
        */
    	struct rte_flow_error error;
    	struct rte_flow_item_ipv4 ipv4_spec;
    	struct rte_flow_item_ipv4 ipv4_mask;
    	struct rte_flow_item ipv4_udp_item;
    	struct rte_flow_item ipv4_tcp_item;
    	struct rte_flow_item ipv4_sctp_item;
    	struct rte_flow_item_udp udp_spec;
    	struct rte_flow_item_udp udp_mask;
    	struct rte_flow_item udp_item;
    	struct rte_flow_item_tcp tcp_spec;
    	struct rte_flow_item_tcp tcp_mask;
    	struct rte_flow_item tcp_item;
    	struct rte_flow_item_sctp sctp_spec;
    	struct rte_flow_item_sctp sctp_mask;
    	struct rte_flow_item sctp_item;
    	struct rte_flow_item pattern_ipv4_5tuple[4];
    	struct rte_flow_classify_rule *rule;
    	uint8_t ipv4_proto;
    
    	if (num_classify_rules >= MAX_NUM_CLASSIFY) {
    		printf(
    			"\nINFO:  classify rule capacity %d reached\n",
    			num_classify_rules);
    		return ret;
    	}
    
    	/* set up parameters for validate and add */
    	memset(&ipv4_spec, 0, sizeof(ipv4_spec));
    	// 填充ip头部协议字段(上层协议)
    	ipv4_spec.hdr.next_proto_id = ntuple_filter->proto;
    	// 填充ip头部源ip地址
    	ipv4_spec.hdr.src_addr = ntuple_filter->src_ip;
    	// 填充ip头部目的ip地址
    	ipv4_spec.hdr.dst_addr = ntuple_filter->dst_ip;
    	ipv4_proto = ipv4_spec.hdr.next_proto_id;
    
    	// TODO:ipv4_mask的作用是什么?
    	memset(&ipv4_mask, 0, sizeof(ipv4_mask));
    	ipv4_mask.hdr.next_proto_id = ntuple_filter->proto_mask;
    	ipv4_mask.hdr.src_addr = ntuple_filter->src_ip_mask;
    	// 转化为掩码
    	ipv4_mask.hdr.src_addr =
    		convert_depth_to_bitmask(ipv4_mask.hdr.src_addr);
    	ipv4_mask.hdr.dst_addr = ntuple_filter->dst_ip_mask;
    	ipv4_mask.hdr.dst_addr =
    		convert_depth_to_bitmask(ipv4_mask.hdr.dst_addr);
    
    	// 根据ip头部中的协议字段来进行分类处理
    	switch (ipv4_proto) {
    	case IPPROTO_UDP:
    		// 如果是UDP
    		// 匹配IPV4
    		ipv4_udp_item.type = RTE_FLOW_ITEM_TYPE_IPV4;
    		ipv4_udp_item.spec = &ipv4_spec;
    		ipv4_udp_item.mask = &ipv4_mask;
    		ipv4_udp_item.last = NULL;
    
    		// 填充UDP头部
    		// 填充UDP字段源端口号
    		udp_spec.hdr.src_port = ntuple_filter->src_port;
    		// 填充UDP字段目的端口号
    		udp_spec.hdr.dst_port = ntuple_filter->dst_port;
    		// 填充UDP字段数据长度
    		udp_spec.hdr.dgram_len = 0;
    		// 填充UDP字段数据校验和
    		udp_spec.hdr.dgram_cksum = 0;
    
    		// 填充udp的掩码
    		udp_mask.hdr.src_port = ntuple_filter->src_port_mask;
    		udp_mask.hdr.dst_port = ntuple_filter->dst_port_mask;
    		udp_mask.hdr.dgram_len = 0;
    		udp_mask.hdr.dgram_cksum = 0;
    
    		// 匹配UDP
    		udp_item.type = RTE_FLOW_ITEM_TYPE_UDP;
    		udp_item.spec = &udp_spec;
    		udp_item.mask = &udp_mask;
    		udp_item.last = NULL;
    
    		// 设置组内规则优先级属性
    		attr.priority = ntuple_filter->priority;
    		// 将每个规则添加到规则数组中
    		pattern_ipv4_5tuple[1] = ipv4_udp_item;
    		pattern_ipv4_5tuple[2] = udp_item;
    		break;
    	case IPPROTO_TCP:
    		// 如果是TCP
    		// 匹配IPV4
    		ipv4_tcp_item.type = RTE_FLOW_ITEM_TYPE_IPV4;
    		ipv4_tcp_item.spec = &ipv4_spec;
    		ipv4_tcp_item.mask = &ipv4_mask;
    		ipv4_tcp_item.last = NULL;
    
    		// 填充TCP头部信息
    		memset(&tcp_spec, 0, sizeof(tcp_spec));
    		// 填充TCP头部字段源端口号
    		tcp_spec.hdr.src_port = ntuple_filter->src_port;
    		// 填充TCP头部字段目的端口号
    		tcp_spec.hdr.dst_port = ntuple_filter->dst_port;
    		
    		// 填充TCP掩码
    		memset(&tcp_mask, 0, sizeof(tcp_mask));
    		tcp_mask.hdr.src_port = ntuple_filter->src_port_mask;
    		tcp_mask.hdr.dst_port = ntuple_filter->dst_port_mask;
    
    		// 匹配TCP
    		tcp_item.type = RTE_FLOW_ITEM_TYPE_TCP;
    		tcp_item.spec = &tcp_spec;
    		tcp_item.mask = &tcp_mask;
    		tcp_item.last = NULL;
    
    		// 设置组内规则优先级
    		attr.priority = ntuple_filter->priority;
    		// 将每个规则添加到规则数组中
    		pattern_ipv4_5tuple[1] = ipv4_tcp_item;
    		pattern_ipv4_5tuple[2] = tcp_item;
    		break;
    	case IPPROTO_SCTP:
    		// 如果是SCTP
    		// 匹配IPV4
    		ipv4_sctp_item.type = RTE_FLOW_ITEM_TYPE_IPV4;
    		ipv4_sctp_item.spec = &ipv4_spec;
    		ipv4_sctp_item.mask = &ipv4_mask;
    		ipv4_sctp_item.last = NULL;
    
    		// 填充SCTP头部字段
    		sctp_spec.hdr.src_port = ntuple_filter->src_port;
    		sctp_spec.hdr.dst_port = ntuple_filter->dst_port;
    		sctp_spec.hdr.cksum = 0;
    		sctp_spec.hdr.tag = 0;
    
    		sctp_mask.hdr.src_port = ntuple_filter->src_port_mask;
    		sctp_mask.hdr.dst_port = ntuple_filter->dst_port_mask;
    		sctp_mask.hdr.cksum = 0;
    		sctp_mask.hdr.tag = 0;
    
    		// 匹配SCTP
    		sctp_item.type = RTE_FLOW_ITEM_TYPE_SCTP;
    		sctp_item.spec = &sctp_spec;
    		sctp_item.mask = &sctp_mask;
    		sctp_item.last = NULL;
    
    		// 将每个规则添加到规则数组中
    		attr.priority = ntuple_filter->priority;
    		pattern_ipv4_5tuple[1] = ipv4_sctp_item;
    		pattern_ipv4_5tuple[2] = sctp_item;
    		break;
    	default:
    		return ret;
    	}
    
    	// 规则适用于入口流量
    	attr.ingress = 1;
    	// 匹配二层数据报文
    	pattern_ipv4_5tuple[0] = eth_item;
    	// 结束匹配
    	pattern_ipv4_5tuple[3] = end_item;
    
    	// 指定action
    	actions[0] = count_action;
    	actions[1] = end_action;
    
    	/* Validate and add rule */
    	/*
    		流分类验证
    		cls_app->cls: 流分类器实例
    		attr: 流规则属性
    		pattern_ipv4_5tuple: 模式指定(列表由END模式项终止)
    		actions: 关联动作(列表由END模式项终止)
    		error: 如果不为NULL,则执行详细的错误报告。仅在发生错误的情况下初始化结构
    	*/
    	ret = rte_flow_classify_validate(cls_app->cls, &attr,
    			pattern_ipv4_5tuple, actions, &error);
    	if (ret) {
    		printf("table entry validate failed ipv4_proto = %u\n",
    			ipv4_proto);
    		return ret;
    	}
    
    	/*
    		将流分类规则添加到flow_classifier表中
    		cls_app->cls: 流分类器实例
    		attr: 流规则属性
    		pattern_ipv4_5tuple: 模式指定(列表由END模式项终止)
    		actions: 关联动作(列表由END模式项终止)
    		key_found: 如果规则已经存在,则返回1,否则返回0
    		error: 如果不为NULL,则执行详细的错误报告。仅在发生错误的情况下初始化结构
    
    		成功时返回有效句柄rule
    	*/
    	rule = rte_flow_classify_table_entry_add(
    			cls_app->cls, &attr, pattern_ipv4_5tuple,
    			actions, &key_found, &error);
    	if (rule == NULL) {
    		printf("table entry add failed ipv4_proto = %u\n",
    			ipv4_proto);
    		ret = -1;
    		return ret;
    	}
    
    	// 将句柄存放在rules数组中
    	rules[num_classify_rules] = rule;
    	num_classify_rules++;
    	return 0;
    }
    
    static int
    add_rules(const char *rule_path, struct flow_classifier *cls_app)
    {
    	FILE *fh;
    	char buff[LINE_MAX];
    	unsigned int i = 0;
    	unsigned int total_num = 0;
    	//用于定义ntuple过滤器条目的结构
    	struct rte_eth_ntuple_filter ntuple_filter;
    	int ret;
    
    	// 打开指定的文件
    	fh = fopen(rule_path, "rb");
    	if (fh == NULL)
    		rte_exit(EXIT_FAILURE, "%s: fopen %s failed\n", __func__,
    			rule_path);
    
    	// 移动到文件头部
    	ret = fseek(fh, 0, SEEK_SET);
    	if (ret)
    		rte_exit(EXIT_FAILURE, "%s: fseek %d failed\n", __func__,
    			ret);
    
    	i = 0;
    	// 循环读取指定的文件的每一行
    	while (fgets(buff, LINE_MAX, fh) != NULL) {
    
    		// 跳过注释和空行
    		if (is_bypass_line(buff))
    			continue;
    
    		if (total_num >= FLOW_CLASSIFY_MAX_RULE_NUM - 1) {
    			printf("\nINFO: classify rule capacity %d reached\n",
    				total_num);
    			break;
    		}
    		
    		// 解析5元组,存放在ntuple_filter中
    		if (parse_ipv4_5tuple_rule(buff, &ntuple_filter) != 0)
    			rte_exit(EXIT_FAILURE,
    				"%s Line %u: parse rules error\n",
    				rule_path, i);
    
    		// 将5元组加入分类规则中
    		if (add_classify_rule(&ntuple_filter, cls_app) != 0)
    			rte_exit(EXIT_FAILURE, "add rule error\n");
    
    		total_num++;
    	}
    
    	fclose(fh);
    	return 0;
    }
    
    /* display usage */
    static void
    print_usage(const char *prgname)
    {
    	printf("%s usage:\n", prgname);
    	printf("[EAL options] --  --"OPTION_RULE_IPV4"=FILE: ");
    	printf("specify the ipv4 rules file.\n");
    	printf("Each rule occupies one line in the file.\n");
    }
    
    /* Parse the argument given in the command line of the application */
    static int
    parse_args(int argc, char **argv)
    {
    	int opt, ret;
    	char **argvopt;
    	int option_index;
    	char *prgname = argv[0];
    	static struct option lgopts[] = {
    		{OPTION_RULE_IPV4, 1, 0, 0},
    		{NULL, 0, 0, 0}
    	};
    
    	argvopt = argv;
    
    	while ((opt = getopt_long(argc, argvopt, "",
    				lgopts, &option_index)) != EOF) {
    
    		switch (opt) {
    		/* long options */
    		case 0:
    			if (!strncmp(lgopts[option_index].name,
    					OPTION_RULE_IPV4,
    					sizeof(OPTION_RULE_IPV4)))
    				parm_config.rule_ipv4_name = optarg;
    			break;
    		default:
    			print_usage(prgname);
    			return -1;
    		}
    	}
    
    	if (optind >= 0)
    		argv[optind-1] = prgname;
    
    	ret = optind-1;
    	optind = 1; /* reset getopt lib */
    	return ret;
    }
    
    /*
     * The main function, which does initialization and calls the lcore_main
     * function.
     */
    int
    main(int argc, char *argv[])
    {
    	struct rte_mempool *mbuf_pool;
    	uint16_t nb_ports;
    	uint16_t portid;
    	int ret;
    	int socket_id;
    	// ACL(访问控制列表)参数
    	struct rte_table_acl_params table_acl_params;
    	// 创建ACL table表参数
    	struct rte_flow_classify_table_params cls_table_params;
    	struct flow_classifier *cls_app;
    	// 流分类器创建参数
    	struct rte_flow_classifier_params cls_params;
    	uint32_t size;
    
    	/* Initialize the Environment Abstraction Layer (EAL). */
    	// 初始化eal层
    	ret = rte_eal_init(argc, argv);
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
    
    	argc -= ret;
    	argv += ret;
    
    	/* parse application arguments (after the EAL ones) */
    	//解析参数
    	ret = parse_args(argc, argv);
    	if (ret < 0)
    		rte_exit(EXIT_FAILURE, "Invalid flow_classify parameters\n");
    
    	/* Check that there is an even number of ports to send/receive on. */
    	// 获取有效网卡的个数
    	nb_ports = rte_eth_dev_count_avail();
    	if (nb_ports < 2 || (nb_ports & 1))
    		rte_exit(EXIT_FAILURE, "Error: number of ports must be even\n");
    
    	/* Creates a new mempool in memory to hold the mbufs. */
    	/* 
    		创建mbuf_pool
    	*/
    	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
    		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
    
    	if (mbuf_pool == NULL)
    		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
    
    	/* Initialize all ports. */
    	RTE_ETH_FOREACH_DEV(portid)
    		if (port_init(portid, mbuf_pool) != 0)
    			rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
    					portid);
    
    	// 只在一个核心上运行
    	if (rte_lcore_count() > 1)
    		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
    
    	socket_id = rte_eth_dev_socket_id(0);
    
    	/* Memory allocation */
    	// 分配一个struct flow_classifier_acl大小的缓存
    	size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct flow_classifier_acl));
    	// malloc后必须调用free,TODO: 和C语言中的malloc类似
    	cls_app = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
    	if (cls_app == NULL)
    		rte_exit(EXIT_FAILURE, "Cannot allocate classifier memory\n");
    
    	// 流分类器参数的name
    	cls_params.name = "flow_classifier";
    	// 流分类器参数的socket_id
    	cls_params.socket_id = socket_id;
    
    	// 创建流分类器,创建成功返回流分类器实例,创建失败返回NULL
    	cls_app->cls = rte_flow_classifier_create(&cls_params);
    	if (cls_app->cls == NULL) {
    		rte_free(cls_app);
    		rte_exit(EXIT_FAILURE, "Cannot create classifier\n");
    	}
    
    	/* initialise ACL table params */
    	// 设置ACL的name字段
    	table_acl_params.name = "table_acl_ipv4_5tuple";
    	// ACL表中的最大规则数,这里的91条
    	table_acl_params.n_rules = FLOW_CLASSIFY_MAX_RULE_NUM;
    	// ACL表中的字段数
    	table_acl_params.n_rule_fields = RTE_DIM(ipv4_defs);
    	// 拷贝ACL表中的规则
    	memcpy(table_acl_params.field_format, ipv4_defs, sizeof(ipv4_defs));
    
    	/* initialise table create params */
    	// ACL表操作ops
    	cls_table_params.ops = &rte_table_acl_ops;
    	// 传递给创建ACL函数的参数,这里是ACL创建的时候传递的结构体
    	cls_table_params.arg_create = &table_acl_params;
    	// 分类表类型
    	cls_table_params.type = RTE_FLOW_CLASSIFY_TABLE_ACL_IP4_5TUPLE;
    	
    	/*
    		流量分类表的创建
    		cls_app->cls: 处理流分类器实例指针
    		cls_table_params: 用于流量分类表参数
    	*/
    	ret = rte_flow_classify_table_create(cls_app->cls, &cls_table_params);
    	if (ret) {
    		rte_flow_classifier_free(cls_app->cls);
    		rte_free(cls_app);
    		rte_exit(EXIT_FAILURE, "Failed to create classifier table\n");
    	}
    
    	/* read file of IPv4 5 tuple rules and initialize parameters
    	 * for rte_flow_classify_validate and rte_flow_classify_table_entry_add
    	 * API's.
    	 */
    	/*
    		读取IPV4的5元组文件,作为rte_flow_classify_validate和rte_flow_classify_table_entry_add的初始化参数
    		parm_config.rule_ipv4_name: 在本程序中主要是--rule_ipv4="../ipv4_rules_file.txt"后面跟的参数ipv4_rules_file.txt
    		cls_app: 自定义数据结构
    	*/
    	if (add_rules(parm_config.rule_ipv4_name, cls_app)) {
    		rte_flow_classifier_free(cls_app->cls);
    		rte_free(cls_app);
    		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
    	}
    
    	/* Call lcore_main on the master core only. */
    	lcore_main(cls_app);
    
    	return 0;
    }
    

    代码的相关流程和说明请看代码中的注释。

    这里简单描述一下该例程实现的功能

    • 首先我们需要至少绑定2的倍数的网卡
    • 创建flow分类器
    • 然后会读取例程中ipv4_rules_file.txt文件(文件中主要是5元组)。将规则绑定到flow分类器中
    • 配置flow分类器中规则的action(该例程中主要是统计的action)
    • 配置网卡的属性(配置发送和接收队列)
    • 将接收到的数据交给flow分类器。如果匹配则统计数据

    这里给出几个网上的参考链接,flow模块比较困难,需要好好的研究。DPDK flow_classify 源码阅读


    本篇文章大量参考了上文链接中的文章相关内容。但是为什么还需要写出来呢?因为链接中的文章只是对于程序作了大致的介绍。由于flow模块非常重要,所以我补充了相关知识梳理的流程图(该流程图主要是描述了分类器的创建和添加规则的流程,以及分类器的组成,个人认为这一部分才是核心):
    分类器流程图

    关于运行的结果我会在后面添加。

    展开全文
  • 改造Caffe原始的分类预测程序classify.py,实现将预测标签输出到文本文件,同时输出的还有图片路径、图片原始标签,以及 Softmax 层输出的原始数据。 支持图片批处理。 具体的数据格式等更详细的介绍请参见我的这...
  • image_classify_using_sift

    2019-01-17 13:37:46
    利用sift++kmeans实现场景分类、图像分类;python代码
  • 文章目录一、Classify Leaves竞赛介绍二、数据分析2.1 训练数据信息统计和查看2.2 测试数据统计和分析2.3 可视化训练数据三、整理 一、Classify Leaves竞赛介绍 描述:(叶子种类分类,总共176类,训练数据18353张图...

    一、Classify Leaves竞赛介绍

    描述:(叶子种类分类,总共176类,训练数据18353张图,测试数据8800张图片,每一类至少有50张图片)

    The task is predicting categories of leaf images. This dataset contains 176 categories, 18353 training images, 8800 test images. Each category has at least 50 images for training. The test set is split evenly into the public and private leaderboard.

    The evaluation metric for this competition is Classification Accuracy.

    Good luck and have fun!

    kanggle竞赛地址:
    https://www.kaggle.com/c/classify-leaves

    二、数据分析

    import pandas as pd 
    import numpy as np 
    from d2l import torch as d2l 
    import seaborn as sns 
    import matplotlib.pyplot as plt 
    from sklearn.preprocessing import LabelEncoder,OneHotEncoder
    import os 
    from PIL import Image 
    from torchvision import transforms
    

    2.1 训练数据信息统计和查看

    # 加载原始数据的统计目录
    train_data = pd.read_csv('./data/classify-leaves/train.csv')
    train_data.head()
    
    imagelabel
    0images/0.jpgmaclura_pomifera
    1images/1.jpgmaclura_pomifera
    2images/2.jpgmaclura_pomifera
    3images/3.jpgmaclura_pomifera
    4images/4.jpgmaclura_pomifera
    # 训练数据共18353张图片,176类叶子
    train_data.describe()
    
    imagelabel
    count1835318353
    unique18353176
    topimages/8006.jpgmaclura_pomifera
    freq1353
    # 统计训练数据label的类别和数量
    train_data['label'].value_counts()
    
    maclura_pomifera            353
    ulmus_rubra                 235
    prunus_virginiana           223
    acer_rubrum                 217
    broussonettia_papyrifera    214
                               ... 
    cedrus_deodara               58
    ailanthus_altissima          58
    crataegus_crus-galli         54
    evodia_daniellii             53
    juniperus_virginiana         51
    Name: label, Length: 176, dtype: int64
    
    # 树叶的名字统计
    labels_unique = train_data['label'].unique()
    labels_unique
    
    array(['maclura_pomifera', 'ulmus_rubra', 'broussonettia_papyrifera',
           'prunus_virginiana', 'acer_rubrum', 'cryptomeria_japonica',
           'staphylea_trifolia', 'asimina_triloba', 'diospyros_virginiana',
           'tilia_cordata', 'ulmus_pumila', 'quercus_muehlenbergii',
           'juglans_cinerea', 'cercis_canadensis', 'ptelea_trifoliata',
           'acer_palmatum', 'catalpa_speciosa', 'abies_concolor',
           'eucommia_ulmoides', 'quercus_montana', 'koelreuteria_paniculata',..., 'sassafras_albidum', 'acer_griseum',
           'ailanthus_altissima', 'pinus_thunbergii', 'crataegus_crus-galli',
           'juniperus_virginiana'], dtype=object)
    
    # 对label进行编码,并将映射表保存下来
    labelencoder = LabelEncoder()
    labelencoder.fit(train_data['label'])
    train_data['label'] = labelencoder.transform(train_data['label'])
    label_map = dict(zip(labelencoder.classes_,labelencoder.transform(labelencoder.classes_)))
    label_inv_map = {v:k for k,v in label_map.items()}
    label_map
    
    {'abies_concolor': 0,
     'abies_nordmanniana': 1,
     'acer_campestre': 2,
     'acer_ginnala': 3,
     'acer_griseum': 4,
     'acer_negundo': 5,
     'acer_palmatum': 6,
     'acer_pensylvanicum': 7,
     'acer_platanoides': 8,
     'acer_pseudoplatanus': 9,
     'acer_rubrum': 10,
     ...
     'zelkova_serrata': 175}
    
    #查看前20类的统计数量和绘图查看
    top20_trainData = train_data['label'].value_counts().sort_values(ascending=False).head(20)
    print(top20_trainData)
    plt.figure(figsize=(15,10))
    sns.barplot(x=top20_trainData.index,y=top20_trainData)
    plt.xticks(rotation=70)
    plt.title("Top 20 categories of leaf statistics")
    plt.show()
    

    2.2 测试数据统计和分析

    test_data = pd.read_csv('./data/classify-leaves/test.csv')
    test_data
    
    image
    0images/18353.jpg
    1images/18354.jpg
    2images/18355.jpg
    3images/18356.jpg
    4images/18357.jpg
    ......
    8795images/27148.jpg
    8796images/27149.jpg
    8797images/27150.jpg
    8798images/27151.jpg
    8799images/27152.jpg

    8800 rows × 1 columns

    # 测试数据共8800张图
    test_data.describe()
    
    image
    count8800
    unique8800
    topimages/20051.jpg
    freq1

    2.3 可视化训练数据

    folder_path = "./data/classify-leaves/"
    
    # 拿出几张照片看看
    fig, ax = plt.subplots(
        nrows=3,
        ncols=4,
        sharex=True,
        sharey=True, 
        figsize=(18,12)
    )
    
    ax = ax.flatten()
    transform = transforms.Compose([
                transforms.Resize((224,224)),
                transforms.ToTensor()
                ])
    for i in range(12):
        img_path = os.path.join(folder_path,train_data['image'][i])
        data = Image.open(img_path)
        data = transform(data)
        ax[i].imshow(data.permute((2,1,0))) # 记住将维度变换一下
        ax[i].set(title=train_data['label'][i])
        ax[i].title.set_size(25)
        
    ax[0].set_xticks([])
    ax[0].set_yticks([])
    plt.tight_layout()
    plt.show()
    

    三、整理

    1. 训练数据和测试数据都是RG图,可以考虑转换为灰度图进行识别(判断颜色特征对数据集是否特别重要)RGB图
    2. 给定的训练数据和测试数据都是规整的大小,但是叶子占据的比例较小,可以考虑进行图片裁减
    3. 训练数据数量不是特别大,可以考虑进行数据增强,扩大数据集数据量小
    4. 由于数据较小,使用k折交叉验证可以得到一个更好的结果
    展开全文
  • cityFunc_classify-源码

    2021-02-22 12:09:32
    cityFunc_classify 这个程序是使用VGG16 + SPP1234结构提取出basic scenes的特征,之后使用xgboost回归此特征; 此称为cls1; 之后在北京市海淀区的街区,上面用选择性搜索算法生成子区域,并使用cls1进行分类; ...
  • LeetCode-classify 用于归类总结其中的一类题型 剑指Offer 3. 数组中重复的数字 4. 二维数组中的查找 5. 替换空格 6. 从尾到头打印链表 7. 重建二叉树(前、中;前、后;中、后) 9.二个栈实现队列 10.1 斐波那契...
  • 1将ux-classify存储库克隆到您选择的文件夹中。 2从以下下载并安装适用于您的操作系统的Node.js: : 3在上面选择的文件夹中打开终端/命令提示符/ bash,或导航到该文件夹​​并输入: npm安装 4从MongoDB网站...
  • SCAN: learning to classify images without labels 阅读笔记概览具体方法实验设置 没用把论文的图片和公式放进来,太懒了 概览 Approach: A two-step approach where feature learning and clustering are ...
  • 将gem ng_classify添加到您的 Gemfile 运行bundle install 创建一个扩展名为.coffee.ng-classify的文件完毕! 配置 您还可以配置组件的命名约定。 在 config/initializers 下创建一个文件 写 NgClassify . setup ...
  • Classify_Homework

    2014-05-05 18:40:18
    用平均样本法,平均距离法,最近邻法和K近邻法进行分类
  • 但是我安装了好几次,都没有models文件夹,也就是没有classify_image这个文件,所以郁闷了好几天,重装数次都没有,windows和树莓派下面都没有 然后就是在GitHub上面寻找一些线索吧,链接: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,816
精华内容 14,326
关键字:

classify