上海站 | 高性能计算之GPU CUDA培训

4月13-15日

三天密集式学习  快速带你晋级
阅读全文
>

正文共13130个字,5张图,预计阅读时间33分钟。

树莓派3B+英特尔神经计算棒进行高速目标检测

NCS Pi

代码:
训练数据预处理:
https://gist.github.com/ahangchen/ae1b7562c1f93fdad1de58020e94fbdf

测试:https://github.com/ahangchen/ncs_detection

Star是一种美德。

background

最近在做一个项目,要在树莓派上分析视频中的图片,检测目标,统计目标个数,这是一张样例图片:

Cattle Counting

Motivation

当下效果最好的目标检测都是基于神经网络来做的,包括faster rcnn, ssd, yolo2等等,要在树莓派这种资源紧张的设备上运行检测模型,首先想到的就是用最轻量的MobileNet SSD,使用Tensorflow object detection api实现的MobileNet SSD虽然已经非常轻,但在树莓派上推导一张1280x720的图仍然需要2秒,有兴趣的同学可以参考这两个项目:

  • armv7版Tensorflow(必须是1.4及以上):https://github.com/lhelontra/tensorflow-on-arm/releases

  • Tensorflow Object detection API: https://github.com/tensorflow/models/tree/master/research/object_detection

具体的操作在Tensorflow文档里都说的很清楚了,在树莓派上的操作也是一样的,有问题可以评论区讨论。

Hardware

极限的模型仍然不能满足性能需求,就需要请出我们今天的主角了,Intel Movidius Neural Computing Stick。

Intel Movidius Neural Computing Stick

处理器 Intel Movidius VPU
支持框架 TensorFlow, Caffe
连接方式 USB 3.0 Type-A
尺寸 USB stick (72.5mm X 27mm X 14mm)
工作温度 0° - 40° C
x86_64 Ubuntu 16.04主机
Raspberry Pi 3B  Stretch desktop
Ubuntu 16.04 虚拟机
系统要求 USB 2.0 以上 (推荐 USB 3.0)
1GB 内存
4GB 存储

实际上这不是一个GPU,而是一个专用计算芯片,但能起到类似GPU对神经网络运算的加速作用。

京东上搜名字可以买到,只要500元左右,想想一块GPU都要几千块钱,就会觉得很值了。

SDK是开源的:https://github.com/movidius/ncsdk

提问不在GitHub issue里,而是在一个专门的论坛:https://ncsforum.movidius.com/

虽然目前NCSDK支持的框架包含Tensorflow和Caffe,但并不是支持所有的模型,目前已支持的模型列表可以在这里查到:https://github.com/movidius/ncsdk/releases

截止到2018年3月15日,NCSDK还没有支持Tensorflow版的MobileNet SSD(比如tf.cast这个操作还未被支持),所以我们需要用Caffe来训练模型,部署到树莓派上。

Environment

ncsdk的环境分为两部分,训练端和测试端。

1、训练端通常是一个Ubuntu 带GPU主机,训练Caffe或TensorFlow模型,编译成NCS可以执行的graph;

2、测试端则面向ncs python mvnc api编程,可以运行在树莓派上raspbian stretch版本,也可以运行在训练端这种机器上。

训练端

安装

安装这个过程,说难不难,也就几行命令的事情,但也有很多坑在训练端主机上,插入神经计算棒,然后:

git clone https://github.com/movidius/ncsdk

cd ncsdk make install

其中,make install干的是这些事情:

  • 检查安装Tensorflow

  • 检查安装Caffe(SSD-caffe)

  • 编译安装ncsdk(不包含inference模块,只包含mvNCCompile相关模块,用来将Caffe或Tensorflow模型转成NCS graph的)

注意,

  • 这些库都是安装到/opt/movidius/这个目录下,并关联到系统python3里边的(/usr/bin/python3),如果你电脑里原来有tf或caffe,也不会被关联上去

  • NCSDK mvNCCompile模块目前只兼容python3,我尝试过将安装完的SDK改成兼容python2的版本,可以将模型编译出来,但是在运行时会报错,所以暂时放弃兼容python2了,也建议大家用默认的python3版本

  • 这个步骤主要的坑来自万恶的Caffe,如果你装过python3版的caffe,大概会有经验一些,这里有几个小坑提示一下:

    • 最好在ncsdk目录中的ncsdk.conf中,开启caffe的cuda支持,即设置CAFFE_USE_CUDA=yes,这样你之后也能用这个caffe来训练模型

    • caffe的依赖会在脚本中安装,但有些Debian兼容问题要解决

    • 开启CUDA支持后,编译caffe会找不到libboost-python3,因为在Ubuntu16.04里,它叫libboost-python3.5,所以要软链接一下:

cd /usr/lib/x86_64-linux-gnu/

sudo ln -s libboost_python-py35.so libboost_python3.so

  • 其他可能出现的caffe的坑,可以在我博客找找答案,如果没有的话,就去caffe的GitHub issue搜吧

测试

一波操作之后,我们装好了ncsdk编译模块,可以下载我训练的caffe模型,尝试编译成ncs graph

git clone https://github.com/ahangchen/MobileNetSSD mvNCCompile example/MobileNetSSD_deploy.prototxt -w MobileNetSSD_deploy.caffemodel -s 12 -is 300 300 -o ncs_mobilenet_ssd_graph

这里其实是调用python3去执行/usr/local/bin/ncsdk/mvNCCompile.py这个文件, 不出意外在当前版本(1.12.00)你会遇到这个错误:

[Error 17] Toolkit Error: Internal Error: Could not build graph. Missing link: conv11_mbox_conf

这是因为NCSDK在处理caffe模型的时候,会把conv11_mbox_conf_new节点叫做conv11_mbox_conf,所以build graph的时候就会找不着。因此需要为这种节点起一个别名,即,将conv11_mbox_conf_new起别名为conv11_mbox_conf,修改SDK代码中的/usr/local/bin/ncsdk/Models/NetworkStage.py,在第85行后面添加:

if ''_new' in name:    self.alias.append(name[:-4])

于是就能编译生成graph了,你会看到一个名为ncs_mobilenet_ssd_graph的文件。

上边这个bug我已经跟NCSDK的工程师讲了,他们在跟进修这个bug:

NCS bug

测试端

NCSDK

测试端要安装ncsdk python api,用于inference,实际上测试端能做的操作,训练端也都能做

git clone https://github.com/movidius/ncsdk

cd api/src

make install

从输出日志可以发现,将ncsdk的lib和include文件分别和系统的python2(/usr/bin/python2)和python3(/usr/bin/python3)做了关联。

然后你可以下一个GitHub工程来跑一些测试:

git clone https://github.com/movidius/ncappzoo

cd ncappzoo/apps/hello_ncs_py python3 hello_ncs.py python2 hello_ncs.py

没报错就是装好了,测试端很简单。

openCV

看pyimagesearch这个教程(https://www.pyimagesearch.com/2017/09/04/raspbian-stretch-install-opencv-3-python-on-your-raspberry-pi/)

Caffe模型训练

就是正常的用caffe训练MobileNet-SSD,主要参考这个仓库:

MobileNet-SSD: https://github.com/chuanqi305/MobileNet-SSD

README里将步骤讲得很清楚了

1、下载SSD-caffe(这个我们已经在NCSDK里装了)

2、下载chuanqi在VOC0712上预训练的模型

3、把MobileNet-SSD这个项目放到SSD-Caffe的examples目录下,这一步可以不做,但是要对应修改train.sh里的caffe目录位置

4、创建你自己的labelmap.prototxt,放到MobileNet-SSD目录下,比如说,你是在coco预训练模型上训练的话,可以把coco的标签文件复制过来,将其中与你的目标类(比如我的目标类是Cattle)相近的类(比如Coco中是Cow)改成对应的名字,并用它的label作为你的目标类的label。

(比如我用21这个类代表Cattle)

1、用你自己的数据训练MobileNet-SSD,参考SSD-caffe的wiki,主要思路还是把你的数据转换成类似VOC或者COCO的格式,然后生成lmdb,坑也挺多的:

假设你的打的标签是这样一个文件raw_label.txt,假装我们数据集只有两张图片:

data/strange_animal/1017.jpg 0.487500   0.320675    0.670000    0.433193 data/strange_animal/1018.jpg 0.215000   0.293952    0.617500    0.481013

我们的目标是将标签中涉及的图片和位置信息转成这样一个目录(在ssd-caffe/data/coco目录基础上生成的):

coco_cattle

├── all # 存放全部图片和xml标签文件 │   ├── 1017.jpg

│   ├── 1017.xml

│   ├── 1018.jpg

│   └── 1018.xml

├── Annotations # 存放全部标签xml

│   ├── 1017.xml

│   └── 1018.xml

├── create_data.sh # 将图片转为lmdb的脚本 ├── create_list.py # 根据ImageSets里的数据集划分文件,生成jpg和xml的对应关系文件到coco_cattle目录下,但我发现这个对应关系文件用不上 ├── images  # 存放全部图片 │   ├── 1017.jpg

│   └── 1018.jpg

├── ImageSets # 划分训练集,验证集和测试集等,如果只想分训练和验证的话,可以把minival.txt,testdev.txt,test.txt内容改成一样的 │   ├── minival.txt

│   ├── testdev.txt

│   ├── test.txt

│   └── train.txt

├── labelmap_coco.prototxt # 如前所述的标签文件,改一下可以放到MobileNet-SSD目录下 ├── labels.txt

├── lmdb # 手动创建这个目录 │   ├── coco_cattle_minival_lmdb # 自动创建的,由图片和标签转换来的LMDB文件 │   ├── coco_cattle_testdev_lmdb

│   ├── coco_cattle_test_lmdb

│   └── coco_cattle_train_lmdb

├── minival.log

├── README.md

├── testdev.log

├── test.log

└── train.log

其中,标签xml的格式如下:

<annotation>

<folder>train</folder>

<filename>86</filename>

<source>

<database>coco_cattle</database>

</source>

<size>

<width>720</width>

<height>1280</height>

<depth>3</depth>

</size>

<segmented>0</segmented>

<object>

<name>21</name>

<pose>Unspecified</pose>

<truncated>0</truncated>

<difficult>0</difficult>

<bndbox>

<xmin>169</xmin>

<ymin>388</ymin>

<xmax>372</xmax>

<ymax>559</ymax>

</bndbox>  </object>

<object>

<name>21</name>

<pose>Unspecified</pose>

<truncated>0</truncated>

<difficult>0</difficult>

<bndbox>

<xmin>169</xmin>

<ymin>388</ymin>

<xmax>372</xmax>

<ymax>559</ymax>

</bndbox>

</object></annotation>

代表一张图中多个对象所在位置(bndbox节点表示),以及类别(name)。

一开始,all, Annotations, images, ImageSets,lmdb四个目录都是空的,你可以把自己的图片放到随便哪个地方,只要在raw_label.txt里写好图片路径就行。

读取raw_label.txt,利用lxml构造一棵dom tree,然后写到Annotations对应的xml里,并将对应的图片移动到image目录里,可以参考这份代码。并根据我们设置的train or not标志符将当前这张图片分配到训练集或测试集中(也就是往ImageSet/train.txt中写对应的图片名)

这样一波操作之后,我们的images和Annotations目录里都会有数据了,接下来我们需要把它们一块复制到all目录下

cp images/* all/

cp Annotations/* all/

然后用create_data.sh将all中的数据,根据ImageSet中的数据集划分,创建训练集和测试集的lmdb,这里对coco的create_data.sh做了一点修改:

cur_dir=$(cd $( dirname ${BASH_SOURCE[0]} ) && pwd ) root_dir=$cur_dir/../..

cd $root_dir

redo=true

# 这里改成all目录

data_root_dir="$cur_dir/all"

# 这里改成自己的数据集名,也是我们这个目录的名字

dataset_name="coco_cattle"

# 指定标签文件mapfile="$root_dir/data/$dataset_name/labelmap_coco.prototxt"

anno_type="detection"

label_type="xml"

db="lmdb"

min_dim=0 max_dim=0 width=0 height=0

extra_cmd="--encode-type=jpg --encoded"

if $redo

then  extra_cmd="$extra_cmd --redo"

fi

for subset in minival testdev train test

do python3 $root_dir/scripts/create_annoset.py --anno-type=$anno_type --label-type=$label_type --label-map-file=$mapfile --min-dim=$min_dim --max-dim=$max_dim --resize-width=$width --resize-height=$height --check-label $extra_cmd $data_root_dir $root_dir/data/$dataset_name/ImageSets/$subset.txt $data_root_dir/../$db/$dataset_name"_"$subset"_"$db examples/$dataset_name 2>&1 | tee $root_dir/data/$dataset_name/$subset.logd

one

于是会lmdb目录下会为每个划分集合创建一个目录,存放数据

├── lmdb

│   ├── coco_cattle_minival_lmdb

│   │   ├── data.mdb

│   │   └── lock.mdb

│   ├── coco_cattle_testdev_lmdb

│   │   ├── data.mdb

│   │   └── lock.mdb

│   ├── coco_cattle_test_lmdb

│   │   ├── data.mdb

│   │   └── lock.mdb

│   └── coco_cattle_train_lmdb

│       ├── data.mdb

│       └── lock.mdb

6、将5生成的lmdb链接到MobileNet-SSD的目录下:

cd MobileNet-SSD ln -s PATH_TO_YOUR_TRAIN_LMDB trainval_lmdb ln -s PATH_TO_YOUR_TEST_LMDB test_lmdb

7、运行gen_model.sh生成三个prototxt(train, test, deploy)

# 默认clone下来的目录是没有example这个目录的,而gen_model.sh又会把文件生成到example目录

mkdir example ./gen_model.sh

8、训练

./train.sh

这里如果爆显存了,可以到example/MobileNetSSD_train.prototxt修改batch size,假如你batch size改到20,刚好可以吃满GTX1060的6G显存,但是跑到一定步数(设置在solver_test.prototxt里的test_interval变量),会执行另一个小batch的test(这个batch size定义在example/MobileNetSSD_test.prototxt里),这样就会再爆显存,所以如果你的train_batch_size + test_batch_size <= 20的话才可以保证你在6G显存上能顺利完成训练,我的设置是train_batch_size=16, test_batch_size=4

一开始的training loss可能比较大,30左右,等到loss下降到2.x一段时间就可以ctrl+c退出训练了,模型权重会自动保存在snapshot目录下

9、运行merge_bn.py将训练得到的模型去除bn层,得到可部署的Caffe模型,这样你就能得到一个名为MobileNetSSD_deploy.caffemodel的权重文件,对应的prototxt为example/MobileNetSSD_deploy.prototxt

10、离题那么久,终于来到主题,我们要把这个caffemodel编译成NCS可运行的graph,这个操作之前在搭环境的部分也提过:

mvNCCompile example/MobileNetSSD_deploy.prototxt -w MobileNetSSD_deploy.caffemodel -s 12 -is 300 300 -o ncs_mobilenet_ssd_graph

参数格式:

mvNCCompile prototxt路径 -w 权重文件路径 -s 最大支持的NCS数目 -is 输入图片宽度 输入图片高度 -o 输出graph路径

其实训练端相对于chuanqi的MobileNet-SSD没啥改动,甚至训练参数也不用怎么改动,主要工作还是在数据预处理上,可以参考我的预处理代码(https://gist.github.com/ahangchen/ae1b7562c1f93fdad1de58020e94fbdf)

树莓派NCS模型测试

现在我们要用ncs版的ssd模型在树莓派上进行对图片做检测,这个目标一旦达成我们自然也能对视频或摄像头数据进行检测了。

仓库结构

ncs_detection

├── data # 标签文件 │   └── mscoco_label_map.pbtxt

├── file_helper.py # 文件操作辅助函数 ├── model # 训练好的模型放在这里 │   ├── ncs_mobilenet_ssd_graph

│   └── README.md

├── ncs_detection.py # 主入口 ├── object_detection # 改了一下TF的Object detection包中的工具类来用 │   ├── __init__.py

│   ├── protos

│   │   ├── __init__.py

│   │   ├── string_int_label_map_pb2.py

│   │   └── string_int_label_map.proto

│   └── utils

│       ├── __init__.py

│       ├── label_map_util.py

│       └── visualization_utils.py

├── r10 # 图片数据 │   ├── 00000120.jpg

│   ├── 00000133.jpg

│   ├── 00000160.jpg

│   ├── 00000172.jpg

│   ├── 00000192.jpg

│   ├── 00000204.jpg

│   ├── 00000220.jpg

│   └── 00000236.jpg

├── README.md

└── total_cnt.txt

由于这个工程一开始是用Tensorflow Object Detection API做的,所以改了其中的几个文件来读标签和画检测框,将其中跟tf相关的代码去掉。

TF的图片IO是用pillow做的,在树莓派上速度奇慢,对一张1280x720的图使用Image的get_data这个函数获取数据需要7秒,所以我改成了OpenCV来做IO。

任务目标

检测r10目录中的图片中的对象,标记出来,存到r10_tmp目录里。

流程

准备目标目录

def config_init(dataset_pref):    os.system('mkdir %s_tmp' % dataset_pref)    os.system('rm %s_tmp/*' % dataset_pref)

指定模型路径,标签位置,类别总数,测试图片路径

PATH_TO_CKPT = 'model/ncs_mobilenet_ssd_graph

'PATH_TO_LABELS = os.path.join('data', 'mscoco_label_map.pbtxt') NUM_CLASSES = 81TEST_IMAGE_PATHS = [os.path.join(img_dir, '%08d.jpg' % i)

for i in range(start_index, end_index)]

发现并尝试打开神经计算棒

def ncs_prepare():    print("[INFO] finding NCS devices...")    devices = mvnc.EnumerateDevices()

if len(devices) == 0:        print("[INFO] No devices found. Please plug in a NCS")        quit()    print("[INFO] found {} devices. device0 will be used. "          "opening device0...".format(len(devices)))    device = mvnc.Device(devices[0])    device.OpenDevice()

return device

将NCS模型加载到NCS中

def graph_prepare(PATH_TO_CKPT, device):    print("[INFO] loading the graph file into RPi memory...")

with open(PATH_TO_CKPT, mode="rb") as f:        graph_in_memory = f.read()

# load the graph into the NCS    print("[INFO] allocating the graph on the NCS...")    detection_graph = device.AllocateGraph(graph_in_memory)

return detection_graph

准备好标签与类名对应关系

category_index = label_prepare(PATH_TO_LABELS, NUM_CLASSES)

读取图片,由于Caffe训练图片采用的通道顺序是RGB,而OpenCV模型通道顺序是BGR,需要转换一下。

image_np = cv2.imread(image_path) image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB)

使用NCS模型为输入图片推断目标位置

def predict(image, graph):    image = preprocess_image(image)    graph.LoadTensor(image, None)    (output, _) = graph.GetResult()    num_valid_boxes = output[0]    predictions = []    for box_index in range(num_valid_boxes):        base_index = 7 + box_index * 7        if (not np.isfinite(output[base_index]) or                not np.isfinite(output[base_index + 1]) or                not np.isfinite(output[base_index + 2]) or                not np.isfinite(output[base_index + 3]) or                not np.isfinite(output[base_index + 4]) or                not np.isfinite(output[base_index + 5]) or                not np.isfinite(output[base_index + 6])):

continue        (h, w) = image.shape[:2]        x1 = max(0, output[base_index + 3])        y1 = max(0, output[base_index + 4])        x2 = min(w, output[base_index + 5])        y2 = min(h, output[base_index + 6])        pred_class = int(output[base_index + 1]) + 1        pred_conf = output[base_index + 2]        pred_boxpts = (y1, x1, y2, x2)        prediction = (pred_class, pred_conf, pred_boxpts)        predictions.append(prediction)    return predictions

其中,首先将图片处理为Caffe输入格式,缩放到300x300,减均值,缩放到0-1范围,转浮点数:

def preprocess_image(input_image):

PREPROCESS_DIMS = (300, 300)

preprocessed = cv2.resize(input_image, PREPROCESS_DIMS)

preprocessed = preprocessed - 127.5

preprocessed = preprocessed * 0.007843

preprocessed = preprocessed.astype(np.float16)

return preprocessed

graph推断得到目标位置,类别,分数

graph.LoadTensor(image, None) (output, _) = graph.GetResult()

其中的output格式为,

[

目标数量,

class,score,xmin, ymin, xmax, ymax,

class,score,xmin, ymin, xmax, ymax,    .

.. ]

根据我们感兴趣的类别和分数进行过滤:

def predict_filter(predictions, score_thresh):    num = 0    boxes = list()    scores = list()    classes = list()

for (i, pred) in enumerate(predictions):        (cl, score, box) = pred

if cl == 21 or cl == 45 or cl == 19 or cl == 76 or cl == 546 or cl == 32:

if score > score_thresh:                boxes.append(box)                scores.append(score)                classes.append(cl)                num += 1    return num, boxes, classes, scores

用OpenCV将当前图片的对象数量写到图片右上角,用pillow(tf库中的实现)将当前图片的对象位置和类别在图中标出:

def add_str_on_img(image, total_cnt):    cv2.putText(image, '%d' % total_cnt, (image.shape[1] - 100, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

result = vis_util.visualize_boxes_and_labels_on_image_array(                image_np,                np.squeeze(valid_boxes).reshape(num, 4),                np.squeeze(valid_classes).astype(np.int32).reshape(num, ),                np.squeeze(valid_scores).reshape(num, ),                category_index,                use_normalized_coordinates=True,                min_score_thresh=score_thresh,                line_thickness=8)

保存图片:

cv2.imwrite('%s_tmp/%s' % (dataset_pref, image_path.split('/')[-1]),                        cv2.cvtColor(result, cv2.COLOR_RGB2BGR))

释放神经计算棒:

def ncs_clean(detection_graph, device):

detection_graph.DeallocateGraph()

device.CloseDevice

()

运行

python2 ncs_detection.py

结果
框架 图片数量/张 耗时
TensorFlow 1800 60min
NCS 1800 10min
TensorFlow 1 2sec
NCS 1 0.3sec

性能提升6倍!单张图300毫秒,可以说是毫秒级检测了。在论坛上有霓虹国的同行尝试后,甚至评价其为“超爆速”。

扩展

单根NCS一次只能运行一个模型,但是我们可以用多根NCS,多线程做检测,达到更高的速度,具体可以看Reference第二条。

Reference

https://www.pyimagesearch.com/2018/02/19/real-time-object-detection-on-the-raspberry-pi-with-the-movidius-ncs/

https://qiita.com/PINTO/items/b97b3334ed452cb555e2

看了这么久,还不快去给我的GitHub(https://github.com/ahangchen/ncs_detection)点star!

原文链接:https://www.jianshu.com/p/3e9862a55e43

查阅更为简洁方便的分类文章以及最新的课程、产品信息,请移步至全新呈现的“LeadAI学院官网”:

www.leadai.org

请关注人工智能LeadAI公众号,查看更多专业文章

大家都在看


LSTM模型在问答系统中的应用

基于TensorFlow的神经网络解决用户流失概览问题

最全常见算法工程师面试题目整理(一)

最全常见算法工程师面试题目整理(二)

TensorFlow从1到2 | 第三章 深度学习革命的开端:卷积神经网络

装饰器 | Python高级编程

今天不如来复习下Python基础

毫秒级检测!你见过带GPU加速的树莓派吗?相关推荐

  1. 60分钟搞定,基于ResNet和Azure GPU加速的肺癌CT图像识别

    用深度学习技术分析医学影像和视频是一个新的研究方向.通过已训练好的卷积神经网络,能很快地搭建并训练自己的深度学习系统. 本文介绍了微软的一个比赛队伍参加2017年Kaggle肺癌CT图像检测比赛,成功 ...

  2. 毫秒级预测,性能卓越!检测、跟踪、行为识别都搞定!这套行人分析系统重磅开源!...

    行人检测跟踪计数.人员行为分析.人员属性分析.人员操作及穿戴合规监测等场景化能力在工业.安防.金融.能源等行业有着极其广泛的应用需求.以深度学习视觉技术为核心的行人分析能力,则是以上任务的核心关键,也 ...

  3. 毫秒级精度计划任务管理、系统运维管理、定时执行任务、定时任务执行、任务定时执行软件 —— 定时执行专家

    目录 一.使用教程 1.软件下载 2.软件的安装方法 3.软件第一次运行,选择界面语言 4.设置软件开机启动,自动运行.自动隐藏(自动隐身运行) 二.软件简介 1.支持 23 种任务类型 2.支持 1 ...

  4. 经典面试题:ES如何做到亿级数据查询毫秒级返回?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:8rr.co/5Csc 面试题 es 在数据量很大的情况下( ...

  5. 百亿数据,毫秒级返回,如何构建?

    前言 大家好,我是坤哥,今天我想和大家聊一聊实时索引的构建之道,来自我司 PB 级索引数据的实战经验,相信对大家肯定有帮助. 近年来公司业务迅猛发展,数据量爆炸式增长,随之而来的的是海量数据查询等带来 ...

  6. linux中shell如何实现毫秒级别的sleep

    方法2.usleep 10000 (usleep为十的负六次方秒,详细见man usleep) 本文转自博客园知识天地的博客,原文链接:linux中shell如何实现毫秒级别的sleep,如需转载请自 ...

  7. Windows平台真实时毫秒级4K H264/H265直播技术方案探讨

    背景 在刚提出4K视频的时候,大多数人都觉得没有必要,4K的出现,意味着更高的硬件规格和传输要求,1080P看的很爽.很清晰,完全满足了日常的需求.随着电视的尺寸越来越大,原本1080P成像已经无法满 ...

  8. arcpy实现空间查询_「实战系列」GP+Roaringbitmap,亿级会员十万级标签毫秒级查询...

    在大数据处理和应用场景中经常需要从亿级甚至十亿级会员中搜索出符合特定标签的会员.很多企业都会使用 HBase 或者 Hive + Hadoop 的方式,这样的方式查询效率非常慢,在标签非常多的情况下计 ...

  9. linux 深度 root,深度刷机让毫秒级一键ROOT成为现实

    如此繁琐,可有便捷的方式?有! 近日,据XDA-Developers论坛一名为Alephzain的会员透露,搭载了 Exynos 4 SoC构架处理器和使用三星内核源的设备存在漏洞,三星的智能机纷纷& ...

最新文章

  1. 2.3.2 进程互斥的软件实现方法
  2. Excel中这四个常出错的地方,你一定中过!
  3. Lambda运行时内部:窥视无服务器巢穴
  4. Rust最受喜爱却少有人用,Python仅排第六,2021全球开发者调查报告出炉
  5. ESP32 + ESP-IDF |GPIO 01 - 驱动外部两个LED灯,以每300ms的时间间隔闪烁
  6. 收集系统性能数据并通过gnuplot绘图
  7. 抽取JDBC工具类:JDBCUtils
  8. Matlab Tricks(二十)—— Hilbert matrix 的创建
  9. python pyquery不规则数据的抓取_11. 数据提取-PyQuery
  10. flutter用英语怎么说_碍手碍脚用英语怎么说?
  11. 基于OpenCore0.5.4/5/6,初级配置视频与文字
  12. 网站克隆工具_全员惊艳!强推5款高质量的小众实用网站
  13. 毕业后5年,我终于变成了月薪13000的软件测试工程师
  14. 假装写在了开头qwq
  15. excel表格打印每页都有表头_分享|1分钟学会,让打印的表格每页自带标题行
  16. getRequestURI()与getRequestURL()
  17. Transfomer XL翻译
  18. 基于Java的航空售票管理系统
  19. 打车出行小程序APP定制开发代驾拼车专车
  20. Unity制作“见缝插针”小游戏

热门文章

  1. php 数组处理函数,PHP数组处理函数举例
  2. Strust2 Mysql数据库,sql语句分页,JSP显示
  3. C语言实验源程序保存,实验一 C语言集成开发环境
  4. spark 写本地文件_(纯干货建议收藏)一次GC引发的Spark调优大全
  5. xposed hook 静态函数_开源Hook框架-epic-实现浅析
  6. 经典的 div + css 鼠标 hover 下拉菜单
  7. 修改MySQL自动递增值
  8. UVA 10055(Hashmat the brave warrior )
  9. 黑盒攻击很难?元学习提高黑盒对抗攻击成功率
  10. OpenGait:首个步态识别框架开源了!