目录

  • 环境下载;点击即可
  • 数据集下载;点击即可
  • 深度学习环境配置
    • 点击下载深度学习环境
    • 数据集准备
    • 使用自己标注的数据集
    • 使用标注软件
    • 数据准备
    • VOC标签格式转yolo格式并划分训练集和测试集
  • 部署和训练深度学习项目
    • 克隆项目
    • 获得预训练权重
    • 训练自己的模型
  • 训练参数详解
  • 启用tensorbord查看参数
  • 输出参数详解
  • 每文一语

本文是作为后续跑深度学习的一个案例教程,可以作为快速搭建自己的深度学习框架,预计按照本教程3~5分钟即可完成,这是因为深度学习是一个框架学习,不像机器学习那样,我们需要进行部署自己的项目代码,做一些较为复杂的算法处理和学习,而深度学习却可以。

深度学习就像人的大脑一样,世界上有很多个人,不同的大脑会有不同的想法,就像牛顿对待熟透落地的苹果一样,诗人面对晚霞下的那一句“落霞与孤鹜齐飞,秋水共长天一色”的宁静致远。

这是就是为什么人有人说他难,也有人它晦涩难懂。

下面我们开始实战项目演练

环境下载;点击即可

数据集下载;点击即可

有需要全套的项目工程;包含训练参数集最佳权重,私信即可

深度学习环境配置

众所周知,深度学习配置环境就是一个比较头疼的事情,本次教程由于是快速部署,默认你是有深度学习环境(pytorch)的,如果没有的话,可以点击下面的链接进行下载,我已经部署好了GPU版本的pytorch的深度学习环境,解压可以使用。

点击下载深度学习环境

解压之后,放在我们的anaconda安装好的目录下;如果你是按照默认路径,如下所示:

然后利用打开我们的anaconda;激活环境;

打开我们的pycharm,按照如下操作进行,将本地的Python工程项目进行和我们刚刚新激活的环境链接起来:

数据集准备

使用自己标注的数据集

由于目前深度学习生态在国内比较的好,应用也比较的广泛,例如在:医疗、农业、交通等领域;当然我们可以使用自己的标注的数据集,也就是说从原始的数据集开始自己标注一张一张的,这样的好处是什么呢?我们可以应用到自己的领域,有些时候网上并不能找到一些标注好的数据集,比如在医学方面,有些疾病的医疗影像是不会公开的,是需要自己进行因地制宜,完成数据的标注的。

使用标注软件

这里主要讲的是在window系统中的安装,首先打开cmd命令行(快捷键:win+R)。进入cmd命令行控制台。输入如下的命令

pip install labelimg -i https://pypi.tuna.tsinghua.edu.cn/simple

运行如上命令后,系统就会自动下载labelimg相关的依赖。由于这是一个很轻量的工具,所以下载起来很快,当出现如下红色框框中的告诉我们成功安装的时候,说明labelimg安装成功了。

数据准备

首先这里需要准备我们需要打标注的数据集。这里我建议新建一个名为VOC2007的文件夹,里面创建一个名为JPEGImages的文件夹存放我们需要打标签的图片文件;再创建一个名为Annotations存放标注的标签文件;最后创建一个名为 predefined_classes.txt 的txt文件来存放所要标注的类别名称。

VOC2007的目录结构为:

├── VOC2007
│├── JPEGImages 存放需要打标签的图片文件
│├── Annotations 存放标注的标签文件
│├── predefined_classes.txt 定义自己要标注的所有类别(这个文件可有可无,但是在我们定义类别比较多的时候,最好有这个创建一个这样的txt文件来存放类别)


打开cmd命令终端(快捷键:win+R)。进入到刚刚创建的这个VOC2007路径(这个很重要,涉及到能不能利用predefined_classes.txt 这个txt文件中定义的类别,我在这里卡了很久,一度以为不能显示txt文件中定义的类别是我安装有问题)。

执行如图中的命令进入到VOC2007路径下(每个人的路径都不一样,按个人的路径去写)如下图所示:可以看到进入到相应的目录了。



上面具体参数我就不说明了,后期会有专门的教程进行解释

通过大量的标注之后,我们会产生大量的标注文件,这里我们使用的是xml格式的,因为比较显著,看起来具有可读性,但是我们yolov5支持的是txt格式的,相信细心的小伙伴已经发现了,所以我们是通常自己标注为xml文件之后,使用脚本批量进行转换

VOC标签格式转yolo格式并划分训练集和测试集

这里就需要对xml格式的标签文件转换为txt文件。同时训练自己的yolov5检测模型的时候,数据集需要划分为训练集和验证集。这里提供了一份代码将xml格式的标注文件转换为txt格式的标注文件,并按比例划分为训练集和验证集。先上代码再讲解代码的注意事项。

import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
import random
from shutil import copyfileclasses = ["标签1", "标签2"]#根据自己标注的进行填写TRAIN_RATIO = 80#训练集比例def clear_hidden_files(path):dir_list = os.listdir(path)for i in dir_list:abspath = os.path.join(os.path.abspath(path), i)if os.path.isfile(abspath):if i.startswith("._"):os.remove(abspath)else:clear_hidden_files(abspath)def convert(size, box):dw = 1./size[0]dh = 1./size[1]x = (box[0] + box[1])/2.0y = (box[2] + box[3])/2.0w = box[1] - box[0]h = box[3] - box[2]x = x*dww = w*dwy = y*dhh = h*dhreturn (x,y,w,h)def convert_annotation(image_id):in_file = open('VOCdevkit/VOC2007/Annotations/%s.xml' %image_id)#输入的文件路径out_file = open('VOCdevkit/VOC2007/YOLOLabels/%s.txt' %image_id, 'w')#输出文件路径tree=ET.parse(in_file)root = tree.getroot()size = root.find('size')w = int(size.find('width').text)h = int(size.find('height').text)for obj in root.iter('object'):difficult = obj.find('difficult').textcls = obj.find('name').textif cls not in classes or int(difficult) == 1:continuecls_id = classes.index(cls)xmlbox = obj.find('bndbox')b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))bb = convert((w,h), b)out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')in_file.close()out_file.close()wd = os.getcwd()
wd = os.getcwd()
data_base_dir = os.path.join(wd, "VOCdevkit/")
if not os.path.isdir(data_base_dir):os.mkdir(data_base_dir)
work_sapce_dir = os.path.join(data_base_dir, "VOC2007/")
if not os.path.isdir(work_sapce_dir):os.mkdir(work_sapce_dir)
annotation_dir = os.path.join(work_sapce_dir, "Annotations/")
if not os.path.isdir(annotation_dir):os.mkdir(annotation_dir)
clear_hidden_files(annotation_dir)
image_dir = os.path.join(work_sapce_dir, "JPEGImages/")
if not os.path.isdir(image_dir):os.mkdir(image_dir)
clear_hidden_files(image_dir)
yolo_labels_dir = os.path.join(work_sapce_dir, "YOLOLabels/")
if not os.path.isdir(yolo_labels_dir):os.mkdir(yolo_labels_dir)
clear_hidden_files(yolo_labels_dir)
yolov5_images_dir = os.path.join(data_base_dir, "images/")
if not os.path.isdir(yolov5_images_dir):os.mkdir(yolov5_images_dir)
clear_hidden_files(yolov5_images_dir)
yolov5_labels_dir = os.path.join(data_base_dir, "labels/")
if not os.path.isdir(yolov5_labels_dir):os.mkdir(yolov5_labels_dir)
clear_hidden_files(yolov5_labels_dir)
yolov5_images_train_dir = os.path.join(yolov5_images_dir, "train/")
if not os.path.isdir(yolov5_images_train_dir):os.mkdir(yolov5_images_train_dir)
clear_hidden_files(yolov5_images_train_dir)
yolov5_images_test_dir = os.path.join(yolov5_images_dir, "val/")
if not os.path.isdir(yolov5_images_test_dir):os.mkdir(yolov5_images_test_dir)
clear_hidden_files(yolov5_images_test_dir)
yolov5_labels_train_dir = os.path.join(yolov5_labels_dir, "train/")
if not os.path.isdir(yolov5_labels_train_dir):os.mkdir(yolov5_labels_train_dir)
clear_hidden_files(yolov5_labels_train_dir)
yolov5_labels_test_dir = os.path.join(yolov5_labels_dir, "val/")
if not os.path.isdir(yolov5_labels_test_dir):os.mkdir(yolov5_labels_test_dir)
clear_hidden_files(yolov5_labels_test_dir)train_file = open(os.path.join(wd, "yolov5_train.txt"), 'w')
test_file = open(os.path.join(wd, "yolov5_val.txt"), 'w')
train_file.close()
test_file.close()
train_file = open(os.path.join(wd, "yolov5_train.txt"), 'a')
test_file = open(os.path.join(wd, "yolov5_val.txt"), 'a')
list_imgs = os.listdir(image_dir) # list image files
prob = random.randint(1, 100)
print("Probability: %d" % prob)
for i in range(0,len(list_imgs)):path = os.path.join(image_dir,list_imgs[i])if os.path.isfile(path):image_path = image_dir + list_imgs[i]voc_path = list_imgs[i](nameWithoutExtention, extention) = os.path.splitext(os.path.basename(image_path))(voc_nameWithoutExtention, voc_extention) = os.path.splitext(os.path.basename(voc_path))annotation_name = nameWithoutExtention + '.xml'annotation_path = os.path.join(annotation_dir, annotation_name)label_name = nameWithoutExtention + '.txt'label_path = os.path.join(yolo_labels_dir, label_name)prob = random.randint(1, 100)print("Probability: %d" % prob)if(prob < TRAIN_RATIO): # train datasetif os.path.exists(annotation_path):train_file.write(image_path + '\n')convert_annotation(nameWithoutExtention) # convert labelcopyfile(image_path, yolov5_images_train_dir + voc_path)copyfile(label_path, yolov5_labels_train_dir + label_name)else: # test datasetif os.path.exists(annotation_path):test_file.write(image_path + '\n')convert_annotation(nameWithoutExtention) # convert labelcopyfile(image_path, yolov5_images_test_dir + voc_path)copyfile(label_path, yolov5_labels_test_dir + label_name)
train_file.close()
test_file.close()

只需要改动上述加了注释的几个地方

最后展示:


可能上述的东西你可以直接通过其他途径获得,但是下面的东西就需要注意了,是重点!

部署和训练深度学习项目

克隆项目

YOLOv5的代码是开源的,因此我们可以从github上克隆其源码。不得不说GitHub的确是全球最大的男性交友网站,里面的人个个都是人才,yolov5发布才一年左右的时间,YOLOv5就已经更新了5个分支了,分别是yolov5.1-yolov5.5分支。该项目就是利用的yolov5.5分支来作为讲解。

首先打开yolov5的github的官网(这个网站在国外打开是很慢的,而且是有的时候能正常打开,有的时候是进不去的,但是大家第一次打不开的话,一定要多打开几次。)打开的官网界面如下,这个就是大神glenn-jocher开源的yolov5的项目。


现在来对代码的整体目录做一个介绍:

├── data:主要是存放一些超参数的配置文件(这些文件(yaml文件)是用来配置训练集和测试集还有验证集的路径的,其中还包括目标检测的种类数和种类的名称);还有一些官方提供测试的图片。如果是训练自己的数据集的话,那么就需要修改其中的yaml文件。但是自己的数据集不建议放在这个路径下面,而是建议把数据集放到yolov5项目的同级目录下面。

├── models:里面主要是一些网络构建的配置文件和函数,其中包含了该项目的四个不同的版本,分别为是s、m、l、x。从名字就可以看出,这几个版本的大小。他们的检测测度分别都是从快到慢,但是精确度分别是从低到高。这就是所谓的鱼和熊掌不可兼得。如果训练自己的数据集的话,就需要修改这里面相对应的yaml文件来训练自己模型。

├── utils:存放的是工具类的函数,里面有loss函数,metrics函数,plots函数等等。

├── weights:放置训练好的权重参数。

├── detect.py:利用训练好的权重参数进行目标检测,可以进行图像、视频和摄像头的检测。

├── train.py:训练自己的数据集的函数。

├── test.py:测试训练的结果的函数。

├──requirements.txt:这是一个文本文件,里面写着使用yolov5项目的环境依赖包的一些版本,可以利用该文本导入相应版本的包。

以上就是yolov5项目代码的整体介绍。我们训练和测试自己的数据集基本就是利用到如上的代码。

数据最好放在最外一级目录中,然后数据集的目录格式如下图所示。大家一定要严格按我的格式来,否则非常容易出问题。

打开requirements.txt这个文件,可以看到里面有很多的依赖库和其对应的版本要求。我们打开pycharm的命令终端,在中输入如下的命令,就可以安装了。

pip install -r requirements.txt

如果你之前使用的是我的环境,那么此处就不需要安装了,我的里面已经安装好了

获得预训练权重

一般为了缩短网络的训练时间,并达到更好的精度,我们一般加载预训练权重进行网络的训练。而yolov5的5.0版本给我们提供了几个预训练权重,我们可以对应我们不同的需求选择不同的版本的预训练权重。通过如下的图可以获得权重的名字和大小信息,可以预料的到,预训练权重越大,训练出来的精度就会相对来说越高,但是其检测的速度就会越慢。预训练权重可以通过这个网址进行下载,本次训练自己的数据集用的预训练权重为yolov5s.pt。

训练自己的模型

预训练模型和数据集都准备好了,就可以开始训练自己的yolov5目标检测模型了,训练目标检测模型需要修改两个yaml文件中的参数。一个是data目录下的相应的yaml文件,一个是model目录文件下的相应的yaml文件。

修改data目录下的相应的yaml文件。找到目录下的voc.yaml文件,将该文件复制一份,将复制的文件重命名,最好和项目相关,这样方便后面操作。

打开这个文件夹修改其中的参数,首先将箭头1中的那一行代码注释掉(我已经注释掉了),如果不注释这行代码训练的时候会报错;箭头2中需要将训练和测试的数据集的路径填上(最好要填绝对路径,有时候由目录结构的问题会莫名奇妙的报错);箭头3中需要检测的类别数,我这里是识别交通标志物,所以这里填写45;最后箭头4中填写需要识别的类别的名字(必须是英文或者自定义ID,否则会乱码识别不出来)。到这里和data目录下的yaml文件就修改好了。

修改模型配置文件

至此,相应的配置参数就修改好了。

如果上面的数据集和两个yaml文件的参数都修改好了的话,就可以开始yolov5的训练了。首先我们找到train.py这个py文件。

然后找到主函数的入口,这里面有模型的主要参数。模型的主要参数解析如下所示。

if __name__ == '__main__':
"""opt模型主要参数解析:--weights:初始化的权重文件的路径地址--cfg:模型yaml文件的路径地址--data:数据yaml文件的路径地址--hyp:超参数文件路径地址--epochs:训练轮次--batch-size:喂入批次文件的多少--img-size:输入图片尺寸--rect:是否采用矩形训练,默认False--resume:接着打断训练上次的结果接着训练--nosave:不保存模型,默认False--notest:不进行test,默认False--noautoanchor:不自动调整anchor,默认False--evolve:是否进行超参数进化,默认False--bucket:谷歌云盘bucket,一般不会用到--cache-images:是否提前缓存图片到内存,以加快训练速度,默认False--image-weights:使用加权图像选择进行训练--device:训练的设备,cpu;0(表示一个gpu设备cuda:0);0,1,2,3(多个gpu设备)--multi-scale:是否进行多尺度训练,默认False--single-cls:数据集是否只有一个类别,默认False--adam:是否使用adam优化器--sync-bn:是否使用跨卡同步BN,在DDP模式使用--local_rank:DDP参数,请勿修改--workers:最大工作核心数--project:训练模型的保存位置--name:模型保存的目录名称--exist-ok:模型目录是否存在,不存在就创建
"""parser = argparse.ArgumentParser()parser.add_argument('--weights', type=str, default='yolov5s.pt', help='initial weights path')parser.add_argument('--cfg', type=str, default='', help='model.yaml path')parser.add_argument('--data', type=str, default='data/coco128.yaml', help='data.yaml path')parser.add_argument('--hyp', type=str, default='data/hyp.scratch.yaml', help='hyperparameters path')parser.add_argument('--epochs', type=int, default=300)parser.add_argument('--batch-size', type=int, default=16, help='total batch size for all GPUs')parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes')parser.add_argument('--rect', action='store_true', help='rectangular training')parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')parser.add_argument('--notest', action='store_true', help='only test final epoch')parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')parser.add_argument('--workers', type=int, default=8, help='maximum number of dataloader workers')parser.add_argument('--project', default='runs/train', help='save to project/name')parser.add_argument('--entity', default=None, help='W&B entity')parser.add_argument('--name', default='exp', help='save to project/name')parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')parser.add_argument('--quad', action='store_true', help='quad dataloader')parser.add_argument('--linear-lr', action='store_true', help='linear LR')parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')parser.add_argument('--upload_dataset', action='store_true', help='Upload dataset as W&B artifact table')parser.add_argument('--bbox_interval', type=int, default=-1, help='Set bounding-box image logging interval for W&B')parser.add_argument('--save_period', type=int, default=-1, help='Log model after every "save_period" epoch')parser.add_argument('--artifact_alias', type=str, default="latest", help='version of dataset artifact to be used')opt = parser.parse_args()

训练自己的模型需要修改如下几个参数就可以训练了。首先将weights权重的路径填写到对应的参数里面,然后将修好好的models模型的yolov5s.yaml文件路径填写到相应的参数里面,最后将data数据的hat.yaml文件路径填写到相对于的参数里面。这几个参数就必须要修改的参数。

到了这一步差不多都可以,训练了

如果运行之后出现报错,直接百度即可,都是可以解决的

训练的时候,我们发现为啥这些指标都是0,通过一番捣鼓之后,发现是cuda的版本安装的不一样

通过一系列的排查纠错,最终终于发现了问题

如果在训练的时候,模型可以跑通,但是就是输出为0,给大家总结了一下几个原因:

  • 有可能是pytorch的cuda版本和你主机的版本不对应,但是这种情况一般出现的几率比较小
  • 建议在训练的时候统一模型参数,选择的时候需要注意有些参数需要修改,深度学习虽然不用写框架,但是参数也不是一成不变的,比如模型的超参数选择,用对应的权重的时候,有对应的参数需要修改。

    这些都可以在官方仓库里面找到,然后修改对应的参数即可;比如第一个是权重文件,根据先验知识选择之后,然后修改对应的cfg文件,记住这里最好和权重选择的对应在一起,其次就是data.yaml文件修改

这里可以选择不同的参数集


这里配置数据集的路径和标签

  • 数据的问题,注意维度是否一致,标签是否存在

训练参数详解

https://www.yht7.com/news/208540
https://zhuanlan.zhihu.com/p/366225937
https://zhuanlan.zhihu.com/p/516662016

启用tensorbord查看参数

yolov5里面有写好的tensorbord函数,可以运行命令就可以调用tensorbord,然后查看tensorbord了。首先打开pycharm的命令控制终端,输入如下命令,就会出现一个网址地址,将那行网址复制下来到浏览器打开就可以看到训练的过程了

切换到该工程路径下

tensorboard --logdir=runs/train

后面就可以让它慢慢训练了,然后进行测试

深度学习比较吃显卡,建议使用台式机跑,本人用的是3060

输出参数详解

https://blog.csdn.net/thy0000/article/details/125281995

https://www.cnblogs.com/Flat-White/p/14724045.html

每文一语

开始也是一种学习

深度学习之初识篇——小白也能跑通的深度学习万能框架【交通标识牌检测】相关推荐

  1. 【零基础强化学习】3个模块教你跑通基于DQN的FlappyBird

    3个模块教你跑通基于DQN的FlappyBird

  2. Redis学习笔记①基础篇_Redis快速入门

    若文章内容或图片失效,请留言反馈.部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 资料链接:https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA( ...

  3. ASP.NET MVC学习之路由篇(2)

    ASP.NET MVC学习之路由篇(2) 原文:ASP.NET MVC学习之路由篇(2) 继ASP.NET MVC学习之路由篇(1)后继续学习. 7.解决与物理路径的冲突 当发送一个请求至ASP.NE ...

  4. c语言学习自我评价,学习自我评价4篇

    学习自我评价4篇 发布时间:2019-04-08 在这次研究性学习中,我们小组在老师的指导下,通过全体成员的协作分工.研究探讨,集全体之智慧,个人之所学,充分将课本中的理论知识与生活实践相结合,用理论 ...

  5. 极客HTTP协议学习笔记破冰篇(1-7)

    极客HTTP协议学习笔记破冰篇(1-7) 前言 各篇章笔记链接 一.学习笔记 1.HTTP的前世今生 2.HTTP是什么 3.与HTTP相关的各种概念(上) 4.与HTTP相关的各种概念(下) 5.常 ...

  6. 【50页/附下载】5G最新进展深度解析—技术应用篇

    | 文章版权所有,未经授权请勿转载或使用 全球5G发展如火如荼,「5G行业应用」继<5G最新进展深度解析--全球市场篇>.<5G最新进展深度解析--国内市场篇>之后,本期推出& ...

  7. 【46页/附下载】5G最新进展深度解析—国内市场篇

    | 文章版权所有,未经授权请勿转载或使用 全球5G发展如火如荼,「5G行业应用」继<5G最新进展深度解析--全球市场篇>之后,继续推出<5G最新进展深度解析--国内市场篇>.& ...

  8. 前端学习路线:从小白到大神的一步之遥

    前端学习路线:从小白到大神的一步之遥 基础学习顺序 用一套图展现 详细学习内容 基础扩展 node.js 框架 vue React 参考资料 基础学习顺序 HTML.CSS.JavaScript 这三 ...

  9. 大话卷积神经网络CNN,小白也能看懂的深度学习算法教程,全程干货建议收藏!...

    来源 | 程序员管小亮 本文创作的主要目的,是对时下最火最流行的深度学习算法的基础知识做一个简介,作者看过许多教程,感觉对小白不是特别友好,尤其是在踩过好多坑之后,于是便有了写这篇文章的想法. 由于文 ...

最新文章

  1. C++ 笔记(31)— 类继承
  2. JavaScript(八)
  3. Metrics.NET 项目
  4. vs2017 不能加载.vdproj
  5. 详细解读Jquery各Ajax函数:$.get(),$.post(),$.ajax(),$.getJSON()
  6. dev多行注释_Dev使用技巧
  7. C++学习基础三——迭代器基础
  8. 每个Java应用容器都要包含tomcat_部署一个不依赖tomcat容器的应用
  9. oracle .ctl 是什么文件_Oracle误删dual表怎么办?这里教你怎么恢复
  10. 数据库人员面试:SQL Server常用测试题
  11. 数据库中where与having区别~~~
  12. 基于vue2.0打造移动商城页面实践 vue实现商城购物车功能 基于Vue、Vuex、Vue-router实现的购物商城(原生切换动画)效果...
  13. 这年头,千万不要花钱创业
  14. PostgreSQL数据库中跨库访问解决方案
  15. day8--pandas
  16. 富其云ERP学习笔记
  17. EditPlus怎么首行和末尾统一添加文本内容
  18. Automated System Call Filtering for Commodity Software 翻译
  19. Error in Summary.factor ‘min’ not meaningful for factors
  20. 抖音SEO,抖音seo优化

热门文章

  1. 网络流和棒球赛淘汰问题 公平分配模板 足球联赛 UVa1306 The K-League
  2. java英文介绍范文_java英语面试自我介绍范本
  3. 反激式隔离AC-DC 开关电源设计(5V 2.4A)资料
  4. 自然语言处理之BERT
  5. 深度学习书籍推荐2019
  6. 计算机网络第一讲——计算机网络的概念与功能
  7. Django LANGUAGE_CODE和TIME_ZONE设置
  8. 【论文阅读】聚集多个启发式信号作为监督用于无监督作文自动评分
  9. php Excel导入导出 , 及看及用
  10. 腾讯云CDB回档失败浅析