21个项目玩转深度学习:基于tensorflow的实践详解06—人脸检测和识别——mtcnn人脸检测(代码片段)

helloworld0604 helloworld0604     2023-01-10     810

关键词:

本篇主要讲述利用MTCNN的预训练模型得到原图中人脸的分割,代码如下:https://github.com/davidsandberg/facenet

结合博客https://blog.csdn.net/FortiLZ/article/details/81396566?tdsourcetag=s_pctim_aiomsg看起来省力些

要是对MTCNN的训练过程感兴趣的,可以看https://www.cnblogs.com/helloworld0604/p/9808795.html

MTCNN原理

经过P,R,O三层网络,一层层检测,具体网络构建如下:

PNet

技术分享图片
class PNet(Network):
    def setup(self):
        (self.feed(data) #pylint: disable=no-value-for-parameter, no-member
             .conv(3, 3, 10, 1, 1, padding=VALID, relu=False, name=conv1)
             .prelu(name=PReLU1)
             .max_pool(2, 2, 2, 2, name=pool1)
             .conv(3, 3, 16, 1, 1, padding=VALID, relu=False, name=conv2)
             .prelu(name=PReLU2)
             .conv(3, 3, 32, 1, 1, padding=VALID, relu=False, name=conv3)
             .prelu(name=PReLU3)
             .conv(1, 1, 2, 1, 1, relu=False, name=conv4-1)
             .softmax(3,name=prob1))

        (self.feed(PReLU3) #pylint: disable=no-value-for-parameter
             .conv(1, 1, 4, 1, 1, relu=False, name=conv4-2))
View Code

技术分享图片

RNet

技术分享图片
class RNet(Network):
    def setup(self):
        (self.feed(data) #pylint: disable=no-value-for-parameter, no-member
             .conv(3, 3, 28, 1, 1, padding=VALID, relu=False, name=conv1)
             .prelu(name=prelu1)
             .max_pool(3, 3, 2, 2, name=pool1)
             .conv(3, 3, 48, 1, 1, padding=VALID, relu=False, name=conv2)
             .prelu(name=prelu2)
             .max_pool(3, 3, 2, 2, padding=VALID, name=pool2)
             .conv(2, 2, 64, 1, 1, padding=VALID, relu=False, name=conv3)
             .prelu(name=prelu3)
             .fc(128, relu=False, name=conv4)
             .prelu(name=prelu4)
             .fc(2, relu=False, name=conv5-1)
             .softmax(1,name=prob1))

        (self.feed(prelu4) #pylint: disable=no-value-for-parameter
             .fc(4, relu=False, name=conv5-2))
View Code

技术分享图片

ONet

技术分享图片
class ONet(Network):
    def setup(self):
        (self.feed(data) #pylint: disable=no-value-for-parameter, no-member
             .conv(3, 3, 32, 1, 1, padding=VALID, relu=False, name=conv1)
             .prelu(name=prelu1)
             .max_pool(3, 3, 2, 2, name=pool1)
             .conv(3, 3, 64, 1, 1, padding=VALID, relu=False, name=conv2)
             .prelu(name=prelu2)
             .max_pool(3, 3, 2, 2, padding=VALID, name=pool2)
             .conv(3, 3, 64, 1, 1, padding=VALID, relu=False, name=conv3)
             .prelu(name=prelu3)
             .max_pool(2, 2, 2, 2, name=pool3)
             .conv(2, 2, 128, 1, 1, padding=VALID, relu=False, name=conv4)
             .prelu(name=prelu4)
             .fc(256, relu=False, name=conv5)
             .prelu(name=prelu5)
             .fc(2, relu=False, name=conv6-1)
             .softmax(1, name=prob1))

        (self.feed(prelu5) #pylint: disable=no-value-for-parameter
             .fc(4, relu=False, name=conv6-2))

        (self.feed(prelu5) #pylint: disable=no-value-for-parameter
             .fc(10, relu=False, name=conv6-3))
View Code

技术分享图片

配置运行环境

export PYTHONPATH=xx/xx/facenet/src    #保证import facenet这句话不出错

参数选项

import argparse
def parse_arguments(argv):
    parser = argparse.ArgumentParser()

    parser.add_argument(input_dir, type=str, help=Directory with unaligned images.)      # 待检测的图片
    parser.add_argument(output_dir, type=str, help=Directory with aligned face thumbnails.)   #检测出的人脸图的保存目录
    parser.add_argument(--image_size, type=int, help=Image size (height, width) in pixels., default=182)    #保存的人脸图大小
    parser.add_argument(--margin, type=int,
                        help=Margin for the crop around the bounding box (height, width) in pixels., default=44)   #检测出人脸后的外围扩充
    parser.add_argument(--random_order,
                        help=Shuffles the order of images to enable alignment using multiple processes., action=store_true)
    parser.add_argument(--gpu_memory_fraction, type=float,
                        help=Upper bound on the amount of GPU memory that will be used by the process., default=1.0)
    parser.add_argument(--detect_multiple_faces, type=bool,
                        help=Detect and align multiple faces per image., default=False) 
    return parser.parse_args(argv)

检测核心代码:src/align/detect_face.py/detect_face函数

def detect_face(img, minsize, pnet, rnet, onet, threshold, factor):
    """Detects faces in an image, and returns bounding boxes and points for them.
    img: input image
    minsize: minimum faces‘ size   20
    pnet, rnet, onet: caffemodel
    threshold: threshold=[th1, th2, th3], th1-3 are three steps‘s threshold  [ 0.6, 0.7, 0.7 ]
    factor: the factor used to create a scaling pyramid of face(图像金字塔) sizes to detect in the image. 0.709
    """
    #print(‘img shape before detect:‘,img.shape)
    factor_count=0
    total_boxes=np.empty((0,9))
    points=np.empty(0)
    h=img.shape[0]
    w=img.shape[1]
    minl=np.amin([h, w])
    m=12.0/minsize
    minl=minl*m
    # create scale pyramid
    scales=[]
    while minl>=12:         #满足这个条件,即min(h,w)>=minsize
        scales += [m*np.power(factor, factor_count)]
        minl = minl*factor
        factor_count += 1
    
    #print(‘scales:‘,scales)  #若min(h,w)==250,则有8个scale,乘过(12/minsize)*250*factor^8<12可理解为250*factor^N<minsize,求N即可    
   # first stage    
    for scale in scales:
     #print(‘******scale:‘,scale)
        hs=int(np.ceil(h*scale))
        ws=int(np.ceil(w*scale))
        im_data = imresample(img, (hs, ws))
        im_data = (im_data-127.5)*0.0078125
        
        #print(‘im_data:‘,im_data.shape)
        img_x = np.expand_dims(im_data, 0)
        img_y = np.transpose(img_x, (0,2,1,3))
        #print(‘img_y (to the pnet):‘,img_y.shape)
        out = pnet(img_y)-----------------------------------------------------------------------第一层用PNet模型预测
        out0 = np.transpose(out[0], (0,2,1,3))
        out1 = np.transpose(out[1], (0,2,1,3))
        #print(‘out0 (after pnet):‘,out0.shape)
        #print("out0",out0[0,:2,:2,:])
        #from ipdb import set_trace;set_trace()
        boxes, _ = generateBoundingBox(out1[0,:,:,1].copy(), out0[0,:,:,:].copy(), scale, threshold[0])  
     ‘‘‘
     最终输出的 boundingbox 是形如 (x, 9),其中前4位是 block 在原图中的坐标,
     第5位是判定为人脸的概率,后4位是 boundingbox regression 的值。
     具体 boundingbox regression 到底是什么,现在还不清楚。
     ‘‘‘
        
        # inter-scale nms 
     ‘‘‘
     NMS (Non-Maximum Suppression):在上述生成的 bb 中,找出判定为人脸概率最大的那个 bb,计算出这个 bb 的面积,
     然后计算其余 bb 与这个 bb 重叠面积的大小,用重叠面积除以:(Min) 两个 bb 中面积较小者;(Union) 两个 bb 的总和面积。
     如果这个值大于 threshold,那么就认为这两个 bb 框的是同一个地方,舍弃判定概率小的;
     如果小于 threshold,则认为两个 bb 框的是不同地方,保留判定概率小的。重复上述过程直至所有 bb 都遍历完成。
     将图片按照所有的 scale 处理过一遍后,会得到在原图上基于不同 scale 的所有的 bb,然后对这些 bb 再进行一次 NMS,并且这次 NMS 的 threshold 要提高。
     ‘‘‘
        pick = nms(boxes.copy(), 0.5, Union)     
        if boxes.size>0 and pick.size>0:
            boxes = boxes[pick,:]
            #print(boxes[:,2]-boxes[:,0])
            #print(boxes[:,3]-boxes[:,1])
            total_boxes = np.append(total_boxes, boxes, axis=0)
        
    #print(‘all scales has done!!!‘)
    numbox = total_boxes.shape[0]
    if numbox>0:
        pick = nms(total_boxes.copy(), 0.7, Union)
        total_boxes = total_boxes[pick,:]
        # 因为pnet的移动大小是12*12,对于一种scale,regw和regh是差不多的
        # 校准bb,得到了真真正正的在原图上 bb 的坐标
        regw = total_boxes[:,2]-total_boxes[:,0]
        regh = total_boxes[:,3]-total_boxes[:,1]
        qq1 = total_boxes[:,0]+total_boxes[:,5]*regw
        qq2 = total_boxes[:,1]+total_boxes[:,6]*regh
        qq3 = total_boxes[:,2]+total_boxes[:,7]*regw
        qq4 = total_boxes[:,3]+total_boxes[:,8]*regh
        total_boxes = np.transpose(np.vstack([qq1, qq2, qq3, qq4, total_boxes[:,4]]))
        total_boxes = rerec(total_boxes.copy())  #调整成正方形
        total_boxes[:,0:4] = np.fix(total_boxes[:,0:4]).astype(np.int32)
        dy, edy, dx, edx, y, ey, x, ex, tmpw, tmph = pad(total_boxes.copy(), w, h)  #把超过原图边界的坐标剪裁一下
        # print(dy[0], edy[0], dx[0], edx[0], y[0], ey[0], x[0], ex[0], tmpw[0], tmph[0])
        # (1, 76, 1, 76, 85, 160, 189, 264, 76, 76)

    #print("***********  second stage  **************")
    numbox = total_boxes.shape[0]   #45
    if numbox>0:
        # second stage
        tempimg = np.zeros((24,24,3,numbox))
        for k in range(0,numbox):
            tmp = np.zeros((int(tmph[k]),int(tmpw[k]),3))
            #tmp生成的图片是高*宽的,rnet的输入要求是宽*高,别忘记转置
            tmp[dy[k]-1:edy[k],dx[k]-1:edx[k],:] = img[y[k]-1:ey[k],x[k]-1:ex[k],:]
            if tmp.shape[0]>0 and tmp.shape[1]>0 or tmp.shape[0]==0 and tmp.shape[1]==0:
                tempimg[:,:,:,k] = imresample(tmp, (24, 24))
            else:
                return np.empty()
        tempimg = (tempimg-127.5)*0.0078125 
        #print(‘temping:‘,tempimg.shape)  #(24, 24, 3, 45)
        tempimg1 = np.transpose(tempimg, (3,1,0,2))
        #print(‘before rnet:‘,tempimg1.shape)  #(45, 24, 24, 3)
        out = rnet(tempimg1)----------------------------------------------------------------第二层用RNet
        out0 = np.transpose(out[0])  #(4,45)
        out1 = np.transpose(out[1])  #(2,45)
        score = out1[1,:]
        ipass = np.where(score>threshold[1])
        #print("ipass:",ipass[0].shape)#(8,)
        total_boxes = np.hstack([total_boxes[ipass[0],0:4].copy(), np.expand_dims(score[ipass].copy(),1)])   #(8,5)
        mv = out0[:,ipass[0]]    #再根据rnet预测出的回归校准bb
        if total_boxes.shape[0]>0:
            pick = nms(total_boxes, 0.7, Union)
            total_boxes = total_boxes[pick,:]   
            #print(‘total_boxes:‘,total_boxes.shape) #(5,5)
            total_boxes = bbreg(total_boxes.copy(), np.transpose(mv[:,pick]))
            total_boxes = rerec(total_boxes.copy())

    #print("***********  third stage  **************")
    numbox = total_boxes.shape[0]  # 5
    if numbox>0:
        # third stage
        total_boxes = np.fix(total_boxes).astype(np.int32)
        dy, edy, dx, edx, y, ey, x, ex, tmpw, tmph = pad(total_boxes.copy(), w, h)
        tempimg = np.zeros((48,48,3,numbox))
        for k in range(0,numbox):
            tmp = np.zeros((int(tmph[k]),int(tmpw[k]),3))
            tmp[dy[k]-1:edy[k],dx[k]-1:edx[k],:] = img[y[k]-1:ey[k],x[k]-1:ex[k],:]
            if tmp.shape[0]>0 and tmp.shape[1]>0 or tmp.shape[0]==0 and tmp.shape[1]==0:
                tempimg[:,:,:,k] = imresample(tmp, (48, 48))
            else:
                return np.empty()
        tempimg = (tempimg-127.5)*0.0078125   #(48, 48, 3, 5)
        tempimg1 = np.transpose(tempimg, (3,1,0,2))
        out = onet(tempimg1)-------------------------------------------------------------------第三层用ONet
        out0 = np.transpose(out[0])   #(4,5)
        out1 = np.transpose(out[1])   #(10,5)
        out2 = np.transpose(out[2])   #(2,5)
        score = out2[1,:]
        points = out1
        ipass = np.where(score>threshold[2])   
        points = points[:,ipass[0]]
        #print("ipass:",ipass[0].shape)#(5,)
        total_boxes = np.hstack([total_boxes[ipass[0],0:4].copy(), np.expand_dims(score[ipass].copy(),1)])
        mv = out0[:,ipass[0]]

        w = total_boxes[:,2]-total_boxes[:,0]+1
        h = total_boxes[:,3]-total_boxes[:,1]+1
        points[0:5,:] = np.tile(w,(5, 1))*points[0:5,:] + np.tile(total_boxes[:,0],(5, 1))-1
        points[5:10,:] = np.tile(h,(5, 1))*points[5:10,:] + np.tile(total_boxes[:,1],(5, 1))-1
        if total_boxes.shape[0]>0:                       #最后一个阶段是先校准再nms,且采用‘min’的方式
            total_boxes = bbreg(total_boxes.copy(), np.transpose(mv))
            pick = nms(total_boxes.copy(), 0.7, Min)
            total_boxes = total_boxes[pick,:]
            points = points[:,pick]
            #print(‘total_boxes:‘,total_boxes.shape) #(1,5)
                
    return total_boxes, points

 

generateBoundingBox(imap, reg, scale, t)

def generateBoundingBox(imap, reg, scale, t):
    # use heatmap to generate bounding boxes
    #print("generateBoundingBox-scale:",scale)
    stride=2
    cellsize=12

    imap = np.transpose(imap)
    dx1 = np.transpose(reg[:,:,0])
    dy1 = np.transpose(reg[:,:,1])
    dx2 = np.transpose(reg[:,:,2])
    dy2 = np.transpose(reg[:,:,3])
    y, x = np.where(imap >= t)   #返回的是另外两维的序号
    #print("y:",y.shape)    # (H,)
    if y.shape[0]==1:            #只有一个概率>threshold的block
        dx1 = np.flipud(dx1)     #上下翻转
        dy1 = np.flipud(dy1)
        dx2 = np.flipud(dx2)
        dy2 = np.flipud(dy2)
    score = imap[(y,x)]          #取可能是人脸的block的概率值
    reg = np.transpose(np.vstack([ dx1[(y,x)], dy1[(y,x)], dx2[(y,x)], dy2[(y,x)] ]))
    #print("reg:",reg.shape) #(H,4)
    if reg.size==0:
        reg = np.empty((0,3))
    bb = np.transpose(np.vstack([y,x]))
    #print("bb:",bb.shape)  #(H,2)
    # 计算原图中的位置
    q1 = np.fix((stride*bb+1)/scale)  #np.fix():Round to nearest integer towards zero.
    q2 = np.fix((stride*bb+cellsize-1+1)/scale)
    boundingbox = np.hstack([q1, q2, np.expand_dims(score,1), reg])   #(H,2)+(H,2)+(H,1)+(H,4)
    #print("boundingbox:",boundingbox.shape)  #(H,9)
    #print(boundingbox[1,:])
    #array([  8.50000000e+01,   1.05000000e+02,   1.03000000e+02,  1.23000000e+02,   6.87459767e-01,  -1.91520154e-02, -7.52890855e-03,  -7.97504187e-03,   2.32149601e-01])
    return boundingbox, reg

nms(boxes, threshold, method)

技术分享图片
def nms(boxes, threshold, method):
    if boxes.size==0:
        return np.empty((0,3))
    x1 = boxes[:,0]
    y1 = boxes[:,1]
    x2 = boxes[:,2]
    y2 = boxes[:,3]
    s = boxes[:,4]
    area = (x2-x1+1) * (y2-y1+1)
    I = np.argsort(s)   #返回排序后的索引
    pick = np.zeros_like(s, dtype=np.int16)
    counter = 0
    while I.size>0:
        i = I[-1]
        pick[counter] = i
        counter += 1
        idx = I[0:-1]
        xx1 = np.maximum(x1[i], x1[idx])
        yy1 = np.maximum(y1[i], y1[idx])
        xx2 = np.minimum(x2[i], x2[idx])
        yy2 = np.minimum(y2[i], y2[idx])
        w = np.maximum(0.0, xx2-xx1+1)
        h = np.maximum(0.0, yy2-yy1+1)
        inter = w * h     # 相交面积
        if method is Min:
            o = inter / np.minimum(area[i], area[idx])
        else:
            o = inter / (area[i] + area[idx] - inter)
        I = I[np.where(o<=threshold)]
    pick = pick[0:counter]   #保留下来的box的序号
    return pick
View Code

得到人脸框坐标和关键点坐标之后,即可以将其裁剪和描绘出来了,可自行学习align_dataset_mtcnn.py中的实现

 

疑惑

自己看的过程中,最为困惑的点就是网络是如何训练的,训练样本是什么样子的,解答都在https://www.cnblogs.com/helloworld0604/p/9808795.html

 

分享《21个项目玩转深度学习:基于tensorflow的实践详解》pdf+源代码

下载:https://pan.baidu.com/s/1NYYpsxbWBvMn9U7jvj6XSw《21个项目玩转深度学习:基于TensorFlow的实践详解》PDF+源代码PDF,378页,带书签目录,文字可以复制。配套源代码。深度学习经典书籍。如图: 查看详情

分享《21个项目玩转深度学习:基于tensorflow的实践详解》pdf+源代码

下载:https://pan.baidu.com/s/1U0B5v5844JMvsGJ22Fjk_Q更多资料:https://www.cnblogs.com/aitf/《21个项目玩转深度学习:基于TensorFlow的实践详解》PDF+源代码PDF,378页,带书签目录,文字可以复制。配套源代码。深度学习经典书籍。 查看详情

分享《21个项目玩转深度学习:基于tensorflow的实践详解》+pdf+源码+何之源

下载:https://pan.baidu.com/s/1U0B5v5844JMvsGJ22Fjk_Q更多资料:http://blog.51cto.com/14087171《21个项目玩转深度学习:基于TensorFlow的实践详解》PDF+源代码PDF,378页,带书签目录,文字可以复制。配套源代码。深度学习经典书籍。如图: 查看详情

分享《21个项目玩转深度学习:基于tensorflow的实践详解》pdf+源代码

下载:https://pan.baidu.com/s/19GwZ9X2E20L3BykhoxhjTg更多资料:http://blog.51cto.com/3215120《21个项目玩转深度学习:基于TensorFlow的实践详解》PDF+源代码PDF,378页,带书签目录,文字可以复制。配套源代码。深度学习经典书籍。如图: 查看详情

分享《21个项目玩转深度学习:基于tensorflow的实践详解》pdf+源代码

...//www.cnblogs.com/javapythonstudy/《21个项目玩转深度学习:基于TensorFlow的实践详解》PDF+源代码PDF,378页,带书签目录,文字可以复制。配套源代码。深度学习经典书籍。如图: 查看详情

《21个项目玩转深度学习:基于tensorflow的实践详解》pdf+源代码

...aidu.com/s/1g4hv05UZ_w92uh9NNNkCaA《21个项目玩转深度学习:基于TensorFlow的实践详解》PDF+源代码PDF,378页,带书签目录,文字可以复制。配套源代码。深度学习经典书籍。如图: 查看详情

21个项目玩转深度学习:基于tensorflow的实践详解06—人脸检测和识别——数据集

书籍:《21个项目玩转深度学习:基于TensorFlow的实践详解》人脸检测FDDBFDDB是UMass的数据集,被用来做人脸检测(FaceDetection)。这个数据集比较大,比较有挑战性。而且作者提供了程序用来评估检测结果,所以在这个数据上面比较... 查看详情

21个项目玩转深度学习:基于tensorflow的实践详解03—打造自己的图像识别模型(代码片段)

...Net,GoogLeNet,VGGNet,ResNet每个方法都有很多版本的衍生,tensorflow中带有封装好各方法和网络的函数,只要喂食自己的训练集就可以完成自己的模型,感觉超方便!!!激动!!!因为虽然原理流程了解了,但 查看详情

21个项目玩转深度学习:基于tensorflow的实践详解06—人脸检测和识别——mtcnn人脸检测(代码片段)

本篇主要讲述利用MTCNN的预训练模型得到原图中人脸的分割,代码如下:https://github.com/davidsandberg/facenet结合博客https://blog.csdn.net/FortiLZ/article/details/81396566?tdsourcetag=s_pctim_aiomsg看起来省力些要是对MTCNN的训练过程感兴趣的,可以看h... 查看详情

深度学习基于卷积神经网络(tensorflow)的人脸识别项目(代码片段)

​活动地址:CSDN21天学习挑战赛目录前言基本思路关于环境OpenCVOpenCV具有的特征OpenCV具有的功能安装OpenCV关键APIimreadnamedWindow示例cv2.VideoCapture(0)CascadeClassifiercap.isOpened()ok,frame=cap.read()cv2.cvtColor(frame,cv2.COLOR_B 查看详情

书籍链接汇总

...下载地址2:网盘下载2.《机器学习实战:基于Scikit-Learn和TensorFlow》高清中文版PDF+高清英文版PDF+源代码下载:https://pan.baidu.com/s/1qKaDd9PSUUGbBQNB3tkDzw3.《深度学习之TensorFlow:入门、原理与进阶实战》,李金洪著下载:https://pan.baidu.c... 查看详情

深度学习基于卷积神经网络(tensorflow)的人脸识别项目(代码片段)

​活动地址:CSDN21天学习挑战赛目录前言基本思路激活函数Sigmoid函数Tanh/双曲正切激活函数ReLU激活函数损失函数作用建立神经网络模型参数说明CNN模型网络结构训练模型训练结果评估模型加载模型评估测试测试结果总结前言... 查看详情

深度学习--tensorflow(项目)识别自己的手写数字(基于cnn卷积神经网络)(代码片段)

目录基础理论 一、训练CNN卷积神经网络1、载入数据2、改变数据维度3、归一化4、独热编码5、搭建CNN卷积神经网络 5-1、第一层:第一个卷积层5-2、第二层:第二个卷积层5-3、扁平化5-4、第三层:第一个全连接层5-5、... 查看详情

基于cnn卷积神经网络的tensorflow+keras深度学习的人脸识别(代码片段)

基于CNN卷积神经网络的TensorFlow+Keras深度学习的人脸识别前言项目实现效果补充模型数据嵌入模型CNN神经网络模型项目概述项目运行流程核心环境配置项目核心代码详解目录核心代码设置数据集目录收集人脸识别数据——UUID格... 查看详情

教程|没有博士学位,照样玩转tensorflow深度学习

香港科技大学TensorFlow三天速成课件https://blog.csdn.net/zhongxon/article/details/54709347香港科技大学TensorFlow三天速成课件https://blog.csdn.net/gh13uy2ql0n5/article/details/78173679机器学习算法总结--线性回归和逻辑回归https://blog.csdn.net/l 查看详情

github上stars最多的53个深度学习项目,tensorflow遥遥领先

...tps://github.com/aymericdamien/TopDeepLearning项目名称Stars项目介绍TensorFlow29622使用数据流图计算可扩展机器学习问题。Caffe11799Caffe是一个高效的开源深度学习框架。NeuralStyle10148Torch实现的神经网络算法。DeepDream9042DeepDream,一款图像识别... 查看详情

深度学习基于卷积神经网络(tensorflow)的人脸识别项目(代码片段)

目录前言基本思路测试人脸识别效果设计思路详细代码加载模型人脸预测主要逻辑测试效果总结所有代码face_predict_use_keras.pykeras_train.pyload_data.pyface_predict_use_keras.py以上就是全部代码啦。有啥疑问就评论区讨论奥。前言经过前段... 查看详情

csdn《深度学习deeplearning》中文版高清完整pdf版下载

...nbsp;提取码: hp39 ,《21个项目玩转深度学习:基于TensorFlow的实践详解》PDF+源代码,绝对收益,希望对你有帮助 参考技术B品品品资源论坛有这本书的PDF版 参考技术C深度学习pdf 看看可不可以用本回答被提问者采纳 参... 查看详情