2018-12-16 13:10:37 haoyujie 阅读数 182

1. 前人的不懈努力。

一直以来,人工智能领域,有两个流派,一个是从底向上,也就是自动感知,自动学习这一个方向,发展到今天能够实现的类似微软的600多层的深度学习的体系。

这里有前人,几十年如一日的信仰和努力。

许多人相信,只要数据量达到一定程度,在不需要理清原理的情况下,实现人工的意识。

2.算力的加强。

这当然是一个非常重要的方向。

3.大量数据的积累。

深度学习,需要大量的数据。才能找出相关性。

这些条件,也在近些年得到了满足。

4.应用的需要。

以色列和中国,对人脸识别,都有着强烈的需求。

========================================

警示:

如下,可以看到,另一条路,也就是符号主义和关联主义者,更加关注理解和语义。

这方面,我们依然没有得到什么实质性的突破,更不要说有什么应用。

而深度学习所对应的计算机视觉,目前来说是最火的相关应用。当然,还有金融、微博等社交应用的数据分析上。

这些应用的特点有几个:

1. 不需要理解原因。如果人脸识别,只要大致猜个差不多就完事了。

2. 不需要太准确。能用就得。

3. 不需要知道背后的机理。因为深度学习,不关心机理。

4.大的公司需要。这恐怕是最严重的一个问题了。

从进入2000年以来,个人的算力,无限地被降低了。

而大公司,大机构,的算力,正在无限地增强。

这些机构的兴趣点,不是科学,不在为个体谋福利,而首先是控制算力,加强自己的算力。

而深度学习,可以说,是臭味相投。

因为深度学习,不是个体玩得起的。大机构浪费得起,哪怕算一亿次,只有有一次命中,它们也能承担。

而传统人工智能,很少的计算,很少的样本,就能够得到重要的结论的方式,是他们绝不愿意看到的。

总之,要结果就好了,不需要原理。

而这类需求,恰恰是大机构所需要的。

个体正相反,比如个体的教育,是长期的、无收益的事情,需要不断地思考其它原理的一种困难的行动。

可以说,深度学习,对个体只有坏处,没有好处,比如更容易被人精准欺骗。

甚至是对个体的生物*武器。而个体,对此,一点办法也没有,因为双方不在一个层面。个体往往有人性,有道德约束,而机构更加类似机器。比如某打车公司,可以把赢利放在安全之上。

2019-10-23 21:22:26 zhengxuqian 阅读数 217

@[TOC](详解深度学习之经典网络:AlexNet(2012) 并利用该网络架构实现人脸识别**)

近来闲来无事,翻出了搁置已久的轻薄版电脑,望着积满灰尘的显示屏,觉得有愧于老师的尊尊教导,心中叹息的声音久久不能平复。    古人云:盛年不重来,一日难再晨。及时宜自勉,岁月不待人。
古人亦云:亡羊补牢,犹未为晚。我辈岂是蓬蒿人?非也。余遂拍案而起,壮士当攻城略地,建功立业,岂可荒戏于杯酒佳肴之间。是日,闻鸡起舞,秉烛夜读,卧薪尝胆,终打通任督二脉,习得这通天彻地之技能----人脸识别。古有周易占卜,今有Al换脸。

好了,不装逼了,再装逼就要被打死了。

今天的主题是使用Python3,keras等搭建AlexNet(2012)

最终的运行结果如下
在这里插入图片描述
本文讲解如何使用Tensorflow搭建一个属于自己的神经网络,并如何进行图像的采集,图像的预处理,神经网络的搭建,以及该模型的训练,参数的计算。并实现一个可以鉴别动态视频流中的人脸的应用,并通过该过程让没有任何机器学习基础的读者了解机器学习使用的场景和流程,包括如何加载模型、如何准备输入数据、如何解析推理结果。
本文默认已搭建好Tensorflow,keras,OpenCV等环境,若没搭建则可以参照其他的博客,此处不再啰嗦。
一,数据的采集
首先进行的是人脸的识别
OpenCV的Haar级联分类器可以通过对比分析相邻图像区域的特征来判断给定图像或子图像与已知对象是否匹配,从而给图像进行分类。提取图像的细节对产生稳定可靠的分类结果很有用。这些提取的结果被称为特征。特征的数量通常应该比像素数少得多。两个图像的相似程度可以通过它们的特征向量的距离来计算。

OpenCV的Haar级联分类器具有尺度不变型(通过循环缩放图像再进行特征比对来实现),即它在尺度缩放上具有鲁棒性。但是,它不具备旋转不变形。例如,Haar级联分离器认为倒置的人脸图像和正立的人脸图像不一样,且认为侧面的人脸图像和正面的人脸图像也不一样。在OpenCV的源代码的副本中会有一个文件夹 \sources\data\haarcascades,但我的是在(D:\Program Files\TenserFlow\pkgs\libopencv-3.4.2-h20b85fd_0\Library\etc\haarcascades)。该文件夹包含了所有OpenCV的人脸检测的XML文件,这些文件可用于检测静止图像、视频和摄像头所得到的图像中的人脸。

在这里插入图片描述这个东西的作用主要是实现对人脸识别的功能,在安装中还有其他的功能,我也一并列在下面:

           人脸检测器(默认):haarcascade_frontalface_default.xml 
           人脸检测器(快速Harr):haarcascade_frontalface_alt2.xml 
           人脸检测器(侧视):haarcascade_profileface.xml 
           眼部检测器(左眼):haarcascade_lefteye_2splits.xml 
           眼部检测器(右眼):haarcascade_righteye_2splits.xml 
           嘴部检测器:haarcascade_mcs_mouth.xml 
           鼻子检测器:haarcascade_mcs_nose.xml 
           身体检测器:haarcascade_fullbody.xml 
           人脸检测器(快速LBP):lbpcascade_frontalface.xml

另外,如果我们想构建自己的分类器,比如识别火焰、汽车,数,花等,我们依然可以使用OpenCV训练构建。

假设我们已将上述文件夹都拷贝到了项目文件夹中。下面的例子我们来检测静止图像中人脸,视频帧流中人脸检测的方法也大致一样

import cv2
import numpy as np
from matplotlib import pyplot as plt

def detect(filename):
	img = cv2.imread(filename) 
    gray =cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

  #检测正脸 
    front_face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
    faces0 = front_face_cascade.detectMultiScale(gray, 1.022, 5)
    print("共检测到%d张人的正脸" %len(faces0)) 

  #检测侧脸
    profile_face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_profileface.xml')
    faces1 = profile_face_cascade.detectMultiScale(gray, 1.2, 6) 
    print("共检测到%d张人的侧脸" %len(faces1)) 
    for (x, y, w, h) in faces0: 
	  cv2.rectangle(img, (x,y), (x+w,y+h), (0,0,255), 1) #画红色矩形框标记正脸      
	for (x, y, w, h) in faces1: 
	  cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 1) #画绿色矩形框标记侧脸
	  return img
img = detect("physicists.jpg")
plt.subplot(1,1,1)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title("OpenCV 人脸检测",fontSize =16, color="b")
plt.show()



附上detectMultiScale函数的参数说明:

detectMultiScale(...) method of cv2.CascadeClassifier instance 
  detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]]) -> objects .
   @brief Detects objects of different sizes in the input image. The detected objects are returned as a list .
   of rectangles. . . @param image Matrix of the type CV_8U containing an image where objects are detected. . 
   @param objects Vector of rectangles where each rectangle contains the detected object, the . 
   rectangles may be partially outside the original image. . 
   @param scaleFactor Parameter specifying how much the image size is reduced at each image scale. . 
   @param minNeighbors Parameter specifying how many neighbors each candidate rectangle should have . to retain it. .
   @param flags Parameter with the same meaning for an old cascade as in the function . cvHaarDetectObjects. It is not used for a new cascade. . @param minSize Minimum possible object size. Objects smaller than that are ignored. .
   @param maxSize Maximum possible object size. Objects larger than that are ignored. If `maxSize == minSize` model is evaluated on single scale. . . The function is parallelized with the TBB library. . . 
   @note . - (Python) A face detection example using cascade classifiers can be found at .
   opencv_source_code/samples/python/facedetect.py

scaleFactor是每次迭代的缩放比例,越小(比1大)越可能检测到更多的人脸,但更可能重复。

minNeighbors 是每个人脸矩形保留尽量数目的最小值,整数。越小越可能检测到更多的人脸。

minSize 和maxSize 可以加入尺寸过滤。

原始图像:
在这里插入图片描述
检测的结果如下:(图中不看镜头的那位大牛是发表“泡利不相容”原理的泡利,被检测出了侧脸)
hi蓝这只是识别静态图片中的人脸,接下来是识别动态视频流中的人脸,只需把图片的来源转化为视频流中的每一帧即可。

 
 
import cv2
import sys
from PIL import Image
 
def CatchUsbVideo(window_name, camera_idx):
    cv2.namedWindow(window_name)
    
    #视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
    cap = cv2.VideoCapture(camera_idx)                
    
    #告诉OpenCV使用人脸识别分类器
    classfier = cv2.CascadeClassifier("./cascades/haarcascade_frontalface_default.xml")
    
    #识别出人脸后要画的边框的颜色,RGB格式
    color = (0, 255, 0)
        
    while cap.isOpened():
        ok, frame = cap.read() #读取一帧数据
        if not ok:            
            break  
 
        #将当前帧转换成灰度图像
        grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)                 
        
        #人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
        faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
        if len(faceRects) > 0:            #大于0则检测到人脸                                   
            for faceRect in faceRects:  #单独框出每一张人脸
                x, y, w, h = faceRect        
                cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
                        
        #显示图像
        cv2.imshow(window_name, frame)        
        c = cv2.waitKey(10)
        if c & 0xFF == ord('q'):
            break        
    
    #释放摄像头并销毁所有窗口
    cap.release()
    cv2.destroyAllWindows() 
    
if __name__ == '__main__':
    if len(sys.argv) != 1:
        print("Usage:%s camera_id\r\n" % (sys.argv[0]))
    else:
        CatchUsbVideo("识别人脸区域", 0)

关于具体每个步骤的含义,初学者可自行百度。
这只是可以在视频流中识别到了人脸,接下来我们需要将这些人脸以图片的格式保存起来

 
 
import cv2
import sys
 
from PIL import Image
 
def CatchPICFromVideo(window_name, camera_idx, catch_pic_num, path_name):
    cv2.namedWindow(window_name)
    
    
    cap = cv2.VideoCapture(camera_idx)                
    
    
    classfier = cv2.CascadeClassifier("./cascades/haarcascade_frontalface_default.xml")
    

    color = (0, 255, 0)
    
    num = 0    
    while cap.isOpened():
        ok, frame = cap.read()
        if not ok:            
            break                
    
        grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  
        
        #人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
        faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
        if len(faceRects) > 0:          #大于0则检测到人脸                                   
            for faceRect in faceRects:  #单独框出每一张人脸
                x, y, w, h = faceRect                        
                
                #将当前帧保存为图片
                img_name = '%s/%d.jpg'%(path_name, num)                
                image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
                cv2.imwrite(img_name, image)                                
                                
                num += 1                
                if num > (catch_pic_num):   #如果超过指定最大保存数量退出循环
                    break
                
                #画出矩形框
                cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
                
                #显示当前捕捉到了多少人脸图片了,这样站在那里被拍摄时心里有个数,不用两眼一抹黑傻等着
                font = cv2.FONT_HERSHEY_SIMPLEX
                cv2.putText(frame,'num:%d' % (num),(x + 30, y + 30), font, 1, (255,0,255),4)                
        
        #超过指定最大保存数量结束程序
        if num > (catch_pic_num): break                
                       
        #显示图像
        cv2.imshow(window_name, frame)        
        c = cv2.waitKey(10)
        if c & 0xFF == ord('q'):
            break        
    
    #释放摄像头并销毁所有窗口
    cap.release()
    cv2.destroyAllWindows() 
    
if __name__ == '__main__':
    if len(sys.argv) != 1:
        print("Usage:%s camera_id face_num_max path_name\r\n" % (sys.argv[0]))
    else:
        CatchPICFromVideo("截取人脸", 0, 1000, 'C:\\Users\\DELL\\Desktop\\zhengfei\\DL\\data')

这段代码我们只是在前面代码的基础上增加脸部图像存储功能,比较简单。

def CatchPICFromVideo(window_name, camera_idx, catch_pic_num, path_name):

在函数定义中,几个参数,分别是窗口名字,摄像头系列号,捕捉照片数量,以及存储路径。根据自己需要进行修改,咱们这里为了精度高一点,选择捕捉1000张脸部照片。在你捕捉的时候由于精度的问题,会捕捉许多非脸部的照片,这时候需要你将不是脸部的照片清洗掉,使数据更加准确。另外,我们还需要捕捉另一个人的图片来提高模型的准确度。然后存储到另一个文件夹下,注意,一个人的照片存储到一个文件夹下,不可弄混。截图完成,就像下图这样。
在这里插入图片描述到此为止,我们已经做好了图像的采集的操作。共2000张脸部图像。
二,数据的预处理

 
import os
import sys
import numpy as np
import cv2
 
IMAGE_SIZE = 64
 
#按照指定图像大小调整尺寸
def resize_image(image, height = IMAGE_SIZE, width = IMAGE_SIZE):
    top, bottom, left, right = (0, 0, 0, 0)
    
    #获取图像尺寸
    h, w, _ = image.shape
    
    #对于长宽不相等的图片,找到最长的一边
    longest_edge = max(h, w)    
    
    #计算短边需要增加多上像素宽度使其与长边等长
    if h < longest_edge:
        dh = longest_edge - h
        top = dh // 2
        bottom = dh - top
    elif w < longest_edge:
        dw = longest_edge - w
        left = dw // 2
        right = dw - left
    else:
        pass 
    
    #RGB颜色
    BLACK = [0, 0, 0]
    
    #给图像增加边界,是图片长、宽等长,cv2.BORDER_CONSTANT指定边界颜色由value指定
    constant = cv2.copyMakeBorder(image, top , bottom, left, right, cv2.BORDER_CONSTANT, value = BLACK)
    
    #调整图像大小并返回
    return cv2.resize(constant, (height, width))
 
#读取训练数据
images = []
labels = []
def read_path(path_name):    
    for dir_item in os.listdir(path_name):
        #从初始路径开始叠加,合并成可识别的操作路径
        full_path = os.path.abspath(os.path.join(path_name, dir_item))
        
        if os.path.isdir(full_path):    #如果是文件夹,继续递归调用
            read_path(full_path)
        else:   #文件
            if dir_item.endswith('.jpg'):
                image = cv2.imread(full_path)                
                image = resize_image(image, IMAGE_SIZE, IMAGE_SIZE)
                
                #放开这个代码,可以看到resize_image()函数的实际调用效果
                #cv2.imwrite('1.jpg', image)
                
                images.append(image)                
                labels.append(path_name)                                
                    
    return images,labels
    
 
#从指定路径读取训练数据
def load_dataset(path_name):
    images,labels = read_path(path_name)    
    
    #将输入的所有图片转成四维数组,尺寸为(图片数量*IMAGE_SIZE*IMAGE_SIZE*3)
    #我和闺女两个人共1200张图片,IMAGE_SIZE为64,故对我来说尺寸为1200 * 64 * 64 * 3
    #图片为64 * 64像素,一个像素3个颜色值(RGB)
    images = np.array(images)
    print(images.shape)    
    
    #标注数据,'liziqiang'文件夹下都是我的脸部图像,全部指定为0,另外一个文件夹下是同学的,全部指定为1
    labels = np.array([0 if label.endswith('liziqiang') else 1 for label in labels])    
    
    return images, labels
 
if __name__ == '__main__':
    if len(sys.argv) != 1:
        print("Usage:%s path_name\r\n" % (sys.argv[0]))    
    else:
        images, labels = load_dataset("C:\\Users\\Administrator\\Desktop\\Python\\data")

稍微解释一下resize_image()函数。这个函数的功能是判断图片是不是正方形,如果不是则增加短边的长度使之变成正方形。这样再调用cv2.resize()函数就可以实现等比例缩放了。因为我们指定缩放的比例就是64 x 64,只有缩放之前图像为正方形才能确保图像不失真。例如:

大概是这么个意思。

将你捕捉到的照片放在俩个不同的文件夹里,我在这里一块放在了data文件夹里。
在这里插入图片描述三,模型的搭建
AlexNet是2012年ImageNet竞赛冠军获得者Hinton和他的学生Alex Krizhevsky设计的。也是在那年之后,更多的更深的神经网络被提出,比如优秀的vgg,GoogLeNet。 这对于传统的机器学习分类算法而言,已经相当的出色。——百度百科

2012年,Alex Krizhevsky、Ilya Sutskever在多伦多大学Geoff Hinton的实验室设计出了一个深层的卷积神经网络AlexNet,夺得了2012年ImageNet LSVRC的冠军,且准确率远超第二名(top5错误率为15.3%,第二名为26.2%),引起了很大的轰动。AlexNet可以说是具有历史意义的一个网络结构,在此之前,深度学习已经沉寂了很长时间,自2012年AlexNet诞生之后,后面的ImageNet冠军都是用卷积神经网络(CNN)来做的,并且层次越来越深,使得CNN成为在图像识别分类的核心算法模型,带来了深度学习的大爆发。
AlexNetba包含了5个卷积层和3个全连接层,模型示意图:
在这里插入图片描述
1.1 96个1010的卷积层
输入数据类型(None,64,64,1)
输出数据类型(None,55,55,96) (55=(64-10)/1+1)
param=10
10396+96=28896
1.2 ReLU激活函数
1.3 最大池化(3*3,s=2)
输入数据类型(None,55,55,96)
输出数据类型(None,27,27,96) (55=(55-3)/2+1)

2.1 256个55的卷积层
输入数据类型(None,27,27,96)
输出数据类型(None,27,27,256)
param=5
596256+256=614656
2.2 ReLU激活函数
2.3 最大池化(3*3,s=2)
输入数据类型(None,27,27,256)
输出数据类型(None,13,13,256) (13=(27-3)/2+1)

3.1 384个33的卷积层
输入数据类型(None,13,13,256)
输出数据类型(None,13,13,384)
param=3
3256384+384=885120
3.2 ReLU激活函数

4.1 384个33的卷积层
输入数据类型(None,13,13,384)
输出数据类型(None,13,13,384)
param=3
3384384+384=1327488
4.2 ReLU激活函数

5.1 256个33的卷积层
输入数据类型(None,13,13,384)
输出数据类型(None,13,13,256)
param=3
3384256+256=884992
5.2 ReLU激活函数
5.3 最大池化(3*3,s=2)
输入数据类型(None,13,13,256)
输出数据类型(None,6,6,256) (6=(13-3)/2+1)

6.1 扁平化 Flatten层
输出数据类型(None,9216)66256=9216

7.1全连接 Dense(4096)
输出数据类型(None,4096)
param=9216*4096=37748736
7.2ReLU激活函数
7.3DropOut(0.5)

8.1全连接 Dense(4096)
输出数据类型(None,4096)
param=4096*4096+4096=16781312
8.2ReLU激活函数
8.3DropOut(0.5)

9.1全连接 Dense(2)
9.2softmax激活函数
各层的参数如下:

 Layer (type)                   Output Shape            Param #   

conv2d_31 (Conv2D)           (None, 55, 55, 96)        28896     
_________________________________________________________________
activation_47 (Activation)   (None, 55, 55, 96)        0         
_________________________________________________________________
max_pooling2d_19 (MaxPooling (None, 27, 27, 96)        0         
_________________________________________________________________
conv2d_32 (Conv2D)           (None, 27, 27, 256)       614656    
_________________________________________________________________
activation_48 (Activation)   (None, 27, 27, 256)       0         
_________________________________________________________________
max_pooling2d_20 (MaxPooling (None, 13, 13, 256)       0         
_________________________________________________________________
conv2d_33 (Conv2D)           (None, 13, 13, 384)       885120    
_________________________________________________________________
activation_49 (Activation)   (None, 13, 13, 384)       0         
_________________________________________________________________
conv2d_34 (Conv2D)           (None, 13, 13, 384)       1327488   
_________________________________________________________________
activation_50 (Activation)   (None, 13, 13, 384)       0         
_________________________________________________________________
conv2d_35 (Conv2D)           (None, 13, 13, 256)       884992    
_________________________________________________________________
activation_51 (Activation)   (None, 13, 13, 256)       0         
_________________________________________________________________
max_pooling2d_21 (MaxPooling (None, 6, 6, 256)         0         
_________________________________________________________________
flatten_5 (Flatten)          (None, 9216)              0         
_________________________________________________________________
dense_17 (Dense)             (None, 4096)              37752832  
_________________________________________________________________
activation_52 (Activation)   (None, 4096)              0         
_________________________________________________________________
dropout_15 (Dropout)         (None, 4096)              0         
_________________________________________________________________
dense_18 (Dense)             (None, 4096)              16781312  
_________________________________________________________________
activation_53 (Activation)   (None, 4096)              0         
_________________________________________________________________
dropout_16 (Dropout)         (None, 4096)              0         
_________________________________________________________________
dense_19 (Dense)             (None, 2)                 8194      
____________________________________________________
Total params: 58,283,490

parameter的计算
卷积层的parameter: 记卷积核的宽为kwk_wkw​,卷积核的高为khk_hkh​,输入这一层的通道数是cinc_{in}cin​,这一层输出的通道数是c_out,则这个卷积层的参数量是(kw∗kh∗cin)∗cout+cout(k_w * k_h * c_{in}) * c_{out} +c_{out}(kw​∗kh​∗cin​)∗cout​+cout​。其中,(kw∗kh∗cin)∗cout(k_w * k_h * c_{in}) * c_{out}(kw​∗kh​∗cin​)∗cout​是权重的数量,最后加的coutc_outco​ut是偏置的数量。可以看出,卷积层的参数量只与卷积核的大小和输入输出的通道数有关。

全连接层的parameter: 因为全连接层中,会把输入先拉伸成一个flat的一维向量,原本feature map中的每个像素都变成一个结点,然后这一层的每个结点都会融合前面所有结点的信息,然后加上偏置。假设这一层的输入拉伸成flat向量之后结点数为ninn_{in}nin​,这一层的输出结点数为noutn_{out}nout​,则这个全连接层的参数量是(nin∗nout)+nout(n_{in} * n_{out}) + n_{out}(nin​∗nout​)+nout​。可以看出,全连接层参数量只与输入输出结点数有关。
代码如下:

import random

 

import numpy as np

from sklearn.model_selection import train_test_split

from keras.preprocessing.image import ImageDataGenerator

from keras.models import Sequential

from keras.layers import Dense, Dropout, Activation, Flatten

from keras.layers import Convolution2D, MaxPooling2D

from keras.optimizers import SGD

from keras.utils import np_utils

from keras.models import load_model

from keras import backend as K

 

from loat_data import load_dataset, resize_image, IMAGE_SIZE

 

 

 

class Dataset:

    def __init__(self, path_name):

        #训练集

        self.train_images = None

        self.train_labels = None

        

        #验证集

        self.valid_images = None

        self.valid_labels = None

        

        #测试集

        self.test_images  = None            

        self.test_labels  = None

        

        #数据集加载路径

        self.path_name    = path_name

        

        #当前库采用的维度顺序

        self.input_shape = None

        

    #加载数据集并按照交叉验证的原则划分数据集并进行相关预处理工作

    def load(self, img_rows = IMAGE_SIZE, img_cols = IMAGE_SIZE, 

             img_channels = 3, nb_classes = 2):

        #加载数据集到内存

        images, labels = load_dataset(self.path_name)        

        

        train_images, valid_images, train_labels, valid_labels = train_test_split(images, labels, test_size = 0.3, random_state = random.randint(0, 100))        

        _, test_images, _, test_labels = train_test_split(images, labels, test_size = 0.5, random_state = random.randint(0, 100))                

        

        #当前的维度顺序如果为'th',则输入图片数据时的顺序为:channels,rows,cols,否则:rows,cols,channels

        #这部分代码就是根据keras库要求的维度顺序重组训练数据集

        if K.image_dim_ordering() == 'th':

            train_images = train_images.reshape(train_images.shape[0], img_channels, img_rows, img_cols)

            valid_images = valid_images.reshape(valid_images.shape[0], img_channels, img_rows, img_cols)

            test_images = test_images.reshape(test_images.shape[0], img_channels, img_rows, img_cols)

            self.input_shape = (img_channels, img_rows, img_cols)            

        else:

            train_images = train_images.reshape(train_images.shape[0], img_rows, img_cols, img_channels)

            valid_images = valid_images.reshape(valid_images.shape[0], img_rows, img_cols, img_channels)

            test_images = test_images.reshape(test_images.shape[0], img_rows, img_cols, img_channels)

            self.input_shape = (img_rows, img_cols, img_channels)            

            

            #输出训练集、验证集、测试集的数量

            print(train_images.shape[0], 'train samples')

            print(valid_images.shape[0], 'valid samples')

            print(test_images.shape[0], 'test samples')

        

            #我们的模型使用categorical_crossentropy作为损失函数,因此需要根据类别数量nb_classes将

            #类别标签进行one-hot编码使其向量化,在这里我们的类别只有两种,经过转化后标签数据变为二维

            train_labels = np_utils.to_categorical(train_labels, nb_classes)                        

            valid_labels = np_utils.to_categorical(valid_labels, nb_classes)            

            test_labels = np_utils.to_categorical(test_labels, nb_classes)                        

        

            #像素数据浮点化以便归一化

            train_images = train_images.astype('float32')            

            valid_images = valid_images.astype('float32')

            test_images = test_images.astype('float32')

            

            #将其归一化,图像的各像素值归一化到0~1区间

            train_images /= 255

            valid_images /= 255

            test_images /= 255            

        

            self.train_images = train_images

            self.valid_images = valid_images

            self.test_images  = test_images

            self.train_labels = train_labels

            self.valid_labels = valid_labels

            self.test_labels  = test_labels

            

#CNN网络模型类            

class Model:

    def __init__(self):

        self.model = None 

        

    #建立模型

    def build_model(self, dataset, nb_classes = 2):

        #构建一个空的网络模型,它是一个线性堆叠模型,各神经网络层会被顺序添加,专业名称为序贯模型或线性堆叠模型

        self.model = Sequential() 

        

        #以下代码将顺序添加CNN网络需要的各层,一个add就是一个网络层

        self.model.add(Convolution2D(96, 10, 10, input_shape = dataset.input_shape))   
        self.model.add(Activation('relu'))                                
        self.model.add(MaxPooling2D(pool_size=(3, 3),strides=2))   
        

        
        
        self.model.add(Convolution2D(256, 5, 5,border_mode='same'))   
        self.model.add(Activation('relu'))                                
        self.model.add(MaxPooling2D(pool_size=(3, 3),strides=2
        
        
        
        self.model.add(Convolution2D(384, 3, 3,border_mode='same'))   
        self.model.add(Activation('relu')) 
        
        
        
        
        self.model.add(Convolution2D(384, 3, 3,border_mode='same'))   
        self.model.add(Activation('relu'))  
        
        
        self.model.add(Convolution2D(256, 3, 3,border_mode='same'))   
        self.model.add(Activation('relu'))  
        self.model.add(MaxPooling2D(pool_size=(3, 3),strides=2)) 
        
        
        self.model.add(Flatten())
        self.model.add(Dense(4096))          
        self.model.add(Activation('relu'))  
        self.model.add(Dropout(0.5))        
        
        self.model.add(Dense(4096))          
        self.model.add(Activation('relu'))  
        self.model.add(Dropout(0.5)) 
        
      

        self.model.add(Dense(nb_classes))                                   #17 Dense层

        self.model.add(Activation('softmax'))                               #18 分类层,输出最终结果

        

        #输出模型概况

        self.model.summary()

        

    #训练模型

    def train(self, dataset, batch_size = 20, nb_epoch = 4, data_augmentation = True):        

        sgd = SGD(lr = 0.01, decay = 1e-6, 

                  momentum = 0.9, nesterov = True) #采用SGD+momentum的优化器进行训练,首先生成一个优化器对象  

        self.model.compile(loss='categorical_crossentropy',

                           optimizer=sgd,

                           metrics=['accuracy'])   #完成实际的模型配置工作

        

        #不使用数据提升,所谓的提升就是从我们提供的训练数据中利用旋转、翻转、加噪声等方法创造新的

        #训练数据,有意识的提升训练数据规模,增加模型训练量

        if not data_augmentation:            

            self.model.fit(dataset.train_images,

                           dataset.train_labels,

                           batch_size = batch_size,

                           nb_epoch = nb_epoch,

                           validation_data = (dataset.valid_images, dataset.valid_labels),

                           shuffle = True)

        #使用实时数据提升

        else:            

            #定义数据生成器用于数据提升,其返回一个生成器对象datagen,datagen每被调用一

            #次其生成一组数据(顺序生成),节省内存,其实就是python的数据生成器

            datagen = ImageDataGenerator(

                featurewise_center = False,             #是否使输入数据去中心化(均值为0),

                samplewise_center  = False,             #是否使输入数据的每个样本均值为0

                featurewise_std_normalization = False,  #是否数据标准化(输入数据除以数据集的标准差)

                samplewise_std_normalization  = False,  #是否将每个样本数据除以自身的标准差

                zca_whitening = False,                  #是否对输入数据施以ZCA白化

                rotation_range = 20,                    #数据提升时图片随机转动的角度(范围为0~180)

                width_shift_range  = 0.2,               #数据提升时图片水平偏移的幅度(单位为图片宽度的占比,0~1之间的浮点数)

                height_shift_range = 0.2,               #同上,只不过这里是垂直

                horizontal_flip = True,                 #是否进行随机水平翻转

                vertical_flip = False)                  #是否进行随机垂直翻转

 

            #计算整个训练样本集的数量以用于特征值归一化、ZCA白化等处理

            datagen.fit(dataset.train_images)                        

 

            #利用生成器开始训练模型

            self.model.fit_generator(datagen.flow(dataset.train_images, dataset.train_labels,

                                                   batch_size = batch_size),

                                     samples_per_epoch = dataset.train_images.shape[0],

                                     nb_epoch = nb_epoch,

                                     validation_data = (dataset.valid_images, dataset.valid_labels))    

    

    MODEL_PATH = './liziqiang.face.model.h5'

    def save_model(self, file_path = MODEL_PATH):

         self.model.save(file_path)

 

    def load_model(self, file_path = MODEL_PATH):

         self.model = load_model(file_path)

 

    def evaluate(self, dataset):

         score = self.model.evaluate(dataset.test_images, dataset.test_labels, verbose = 1)

         print("%s: %.2f%%" % (self.model.metrics_names[1], score[1] * 100))

 

    #识别人脸

    def face_predict(self, image):    

        #依然是根据后端系统确定维度顺序

        if K.image_dim_ordering() == 'th' and image.shape != (1, 3, IMAGE_SIZE, IMAGE_SIZE):

            image = resize_image(image)                             #尺寸必须与训练集一致都应该是IMAGE_SIZE x IMAGE_SIZE

            image = image.reshape((1, 3, IMAGE_SIZE, IMAGE_SIZE))   #与模型训练不同,这次只是针对1张图片进行预测    

        elif K.image_dim_ordering() == 'tf' and image.shape != (1, IMAGE_SIZE, IMAGE_SIZE, 3):

            image = resize_image(image)

            image = image.reshape((1, IMAGE_SIZE, IMAGE_SIZE, 3))                    

        

        #浮点并归一化

        image = image.astype('float32')

        image /= 255

        

        #给出输入属于各个类别的概率,我们是二值类别,则该函数会给出输入图像属于0和1的概率各为多少

        result = self.model.predict_proba(image)

        print('result:', result)

        

        #给出类别预测:0或者1

        result = self.model.predict_classes(image)        

 

        #返回类别预测结果

        return result[0]

 

 

 

 

 

 

 

    

if __name__ == '__main__':

    dataset = Dataset('./data/')    

    dataset.load()

    

    model = Model()

    model.build_model(dataset)

    

    #先前添加的测试build_model()函数的代码

    model.build_model(dataset)

 

    #测试训练函数的代码

    model.train(dataset)

    

    

if __name__ == '__main__':

    dataset = Dataset('./data/')    

    dataset.load()

    

    model = Model()

    model.build_model(dataset)

    model.train(dataset)

    model.save_model(file_path = './model/zhengfei.face.model.h5')

    

    

if __name__ == '__main__':    

    dataset = Dataset('./data/')    

    dataset.load()

 

    

    #评估模型

    model = Model()

    model.load_model(file_path = './model/zhengfei.face.model.h5')

    model.evaluate(dataset)    

    

    

由此可见该网络将近有六千万个参数需要训练,(这种架构算是中量级的)我的CPU温度立马就飙到了100度,如果毕业后有钱了一定搞一块专用的图像处理芯片
在这里插入图片描述训练后的结果如下:

Epoch 1/4
560/560 [==============================] - 7232s 13s/step - loss: 0.4136 - acc: 0.8128 - val_loss: 0.0261 - val_acc: 0.9898
Epoch 2/4
560/560 [==============================] - 1020s 2s/step - loss: 0.0383 - acc: 0.9886 - val_loss: 0.0018 - val_acc: 0.9992
Epoch 3/4
560/560 [==============================] - 3403s 6s/step - loss: 0.0085 - acc: 0.9979 - val_loss: 0.0027 - val_acc: 0.9992
Epoch 4/4
560/560 [==============================] - 1016s 2s/step - loss: 0.0148 - acc: 0.9953 - val_loss: 0.0022 - val_acc: 0.9996
(18018, 64, 64, 3)
12612 train samples
5406 valid samples
9009 test samples
9009/9009 [==============================] - 221s 25ms/step
acc: 99.96%

由于该架构的参数过多,训练的时间大概需要一个小时,耐心等待就好。因为时间过久,在这里只迭代了4次,最终准确率也达到了99.96%
此时的模型也已经搭建好了,接下来就是检验的时候了

四,识别人脸

import cv2
import sys
import gc
from face_train import Model
 
if __name__ == '__main__':
    if len(sys.argv) == 1:
        print("Usage:%s camera_id\r\n" % (sys.argv[0]))
        sys.exit(0)
        
    #加载模型
    model = Model()
    model.load_model(file_path = './model/zhengfei.face.model.h5')    
              
    #框住人脸的矩形边框颜色       
    color = (0, 255, 0)
    
    #捕获指定摄像头的实时视频流
    cap = cv2.VideoCapture(0)
    
    #人脸识别分类器本地存储路径
    cascade_path = "haarcascade_frontalface_alt2.xml"    
    
    #循环检测识别人脸
    while True:
        ret, frame = cap.read()   #读取一帧视频
        
        if ret is True:
            
            #图像灰化,降低计算复杂度
            frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        else:
            continue
        #使用人脸识别分类器,读入分类器
        cascade = cv2.CascadeClassifier(cascade_path)                
 
        #利用分类器识别出哪个区域为人脸
        faceRects = cascade.detectMultiScale(frame_gray, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))        
        if len(faceRects) > 0:                 
            for faceRect in faceRects: 
                x, y, w, h = faceRect
                
                #截取脸部图像提交给模型识别这是谁
                image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
                faceID = model.face_predict(image)   
                
                #如果是“我”
                if faceID == 0:                                                        
                    cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness = 2)
                    
                    #文字提示是谁
                    cv2.putText(frame,'zhengfei', 
                                (x + 30, y + 30),                      #坐标
                                cv2.FONT_HERSHEY_SIMPLEX,              #字体
                                1,                                     #字号
                                (255,0,255),                           #颜色
                                2)#字的线宽
                else:
                    if faceID == 1:
                        cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness = 2)
                    
                    #文字提示是谁
                        cv2.putText(frame,'lilicheng', 
                                (x + 30, y + 30),                      #坐标
                                cv2.FONT_HERSHEY_SIMPLEX,              #字体
                                1,                                     #字号
                                (0,0,255),                           #颜色
                                2)#字的线宽
                    else:
                        pass
                            
        cv2.imshow("识别朕", frame)
        
        #等待10毫秒看是否有按键输入
        k = cv2.waitKey(10)
        #如果输入q则退出循环
        if k & 0xFF == ord('q'):
            break
 
    #释放摄像头并销毁所有窗口
    cap.release()
    cv2.destroyAllWindows()


最终结果:
在这里插入图片描述

2020-03-01 22:07:58 sheep_princess 阅读数 16

四小时
恶补树和图知识点
详细分析见
树知识点小结:https://blog.csdn.net/sheep_princess/article/details/104589059
图知识点小结:https://blog.csdn.net/sheep_princess/article/details/104597219
一小时
PTA刷题
在这里插入图片描述
半小时
学习Java
基本类型初始值
在这里插入图片描述
foreach循环
在这里插入图片描述

2019-10-07 11:13:02 hlzdbk 阅读数 25

  时光荏苒,岁月如梭,七天的时间飞快的过去。我给我的七天总结了一个关键字“变”,那么何为“变”而我又变了什么呢?首先,“变”指的是一些变化,无论是心态上还是行动上我都有了很大的改变。

  第一天的学习带给我的是民族自豪感和对互联网的好奇感,我觉得我变了,对国家的爱变了,对互联网的看法变了,开始变得更爱祖国,对浩瀚的网络更加的好奇,第二天快捷键的学习思维导图软件的使用让我也变了,我开始变得更加的有效率,或者说想着如何能够更加的有效率,如何的在有限的时间做更多的事。第三天做VB训练编程思维我的学习方式又变了,从之前只知道一味地跟着书上的例子敲变成了画出流程图,看着这个程序的逻辑思维去设计去完成程序。之后的几天也带给了我不同的感受,我就不一一列举。

  总之我觉得来到提高班之后我时时刻刻在改变,各个方面也在改变,我相信现在做的每一件事都是有意义的,生活就是学习的过程,就让我们在这个过程之中改变自己。

2017-12-13 17:40:34 Uwr44UOuQcNsUQb60zk2 阅读数 907

机器之心原创

机器之心海外部

作者:Tony Peng、Alex Chen、Qintong Wu、之乎


美国时间周四,NIPS大会走完了日程的一半。工业界的众多公司搬东西撤出了展览会场,受邀演讲也全部结束。之后亮点除了当地时间周五周六的Workshop以外,就是周四下午的四场重要的研讨会——从元学习和深度强化学习,到DeepMind刚刚公布的AlphaZero,以及Yann LeCun参加了NIPS史上第一次辩论,一天的精彩内容尽在此文中。



Kinds of Intelligence:Alpha Zero成全场焦点,认知科学大牛唱反调

Kinds of Intelligence主要讨论了实现智能的多种途径,吸引了众多参会者前来。不少生物界、心理学界和认知科学界的专家分享了人工智能以外的研究。


DeepMind的CEO Demis Hassabis也是此次研讨会的嘉宾。Hassabis上台时,现场雷动。诺达的会议大厅坐无缺席,连走道上都是参会者,逼得保安不得不过来安排下秩序。学术圈的大会热捧工业界的新星,也只有DeepMind能享受这番待遇。



Demis Hassabis

周二,DeepMind公布了其围棋程序的最新迭代AlphaZero。和AlphaGo Zero一样,AlphaZero不需要人类的知识,完全靠自我对弈的强化学习,从零开始。不同的是,AlphaZero拥有更强大的泛化能力,经过不到 24 小时的训练后,Alpha Zero可以在国际象棋和日本将棋上击败目前业内顶尖的AI程序(这些程序早已超越人类世界冠军水平),也可以轻松击败训练 3 天时间的 AlphaGo Zero。


Hassabis先介绍了DeepMind和AlphaGo的发展历程,然后着重介绍了AlphaZero是怎么从围棋泛化到国象和日本将棋上。


目前最前沿的国象AI程序依然使用Alpha-Beta搜索和启发算法。2016年TCEC世界冠军Stockfish是这个领域最好的AI程序,但Stockfish依然需要大量手动调整,包括棋局表征、搜索、落子顺序、评估以及残局库。AlphaZero呢?只需要基于自我对弈强化学习加自我对弈蒙特卡洛树搜索即可。同时,Alpha Zero可以完全在这三种不同的棋类游戏中泛化,三种棋类背后的算法和超参数完全相同。


AlphaZero的战绩是显著的:对阵国象AI程序Stockfish是28胜72平0负;对阵将棋顶尖程序Elmo是90胜2平8负;对阵训练三天的AlphaGo Zero是60胜40负。


Hassabis提到了有关AlphaZero的几个有趣的事实:下棋的每个决定都需要一定的搜索量。人类一般是10次,目前最先进的国象程序是1000万次,AlphaZero则是10000次,介于两者之间;AlphaZero下国象中,摈弃了许多人类的招数,比如Kings Indian Defence、French Defence和Queen Pawn Game,这三种都是很常见的国象开局方式;AlphaZero偏爱长期位置牺牲(long-term positional sacrifices),为了最后的赢面在前期牺牲掉部分棋子。


拥有更好泛化能力的AlphaZero让很多人相信人类离通用人工智能(AGI)又进了一步。然而,其他几位受邀嘉宾并不买账。东海岸的两位认知科学大牛——纽约大学的Gary Marcus和来自麻省理工大学的Josh Tenenbaum都不认为AlphaZero代表了AGI的研究方向,


Tenenbaum认为智能不仅仅是将一个公式计算的特别好,而是思考到底解决什么样的问题。他提出了一个非常有意思的想法:建造一个像小孩子一样学习的机器。


Tenenbaum向观众展示了一个视频:一个小孩看到大人双手捧着书,在一个关着门的书橱前踱步,小孩很自觉地走上前把门打开。这样的理解能力和操作能力,是机器做不到的。要建造这样的机器,需要三个步骤:建立一个具备常识的核心;用这个核心学习语言;用语言学习任何东西。


之后登场的Marcus,更是圈内有名的“辩论手”(详细参见他十月和Yann LeCun的争论)。此次演讲,他还专门为了AlphaZero做了篇PPT:AlphaZero只适用于完美信息的游戏博弈中,但并不适合通用人工智能。


Marcus提出了一个有关“认知”的公式:Cognition=f(a, k ,e),其中a代表先天的算法,k代表先天知识,e代表经验。这个公式同样可以适用于Alpha Zero。完美信息棋盘博弈获得成功的条件是棋盘规则+经过人类编程的棋局表征,a则是强化学习+蒙特卡洛树搜索+基于经验得到的超参数,即使没有了先天知识k,AlphaZero同样获得了成功。



但无论是围棋、将棋、国象,都和生活是不一样的:棋局是完美信息,但生活是不完美信息;棋局可以被完美模拟,生活却不可能;棋局里可以有无限的数据,而生活里的每个事物的数据量都不多;棋局里唯一要紧的是盘面状态,但生活里,什么都有要紧。


所以,在一个开放的世界里,先天的算法和先天的知识需求量都会增加,就不是Alph Zero可以应付的了。


另一个Marcus从DeepMind中得到的结果是,即使是在完美信息的游戏中,一些先天的结构依然不可缺少,比如蒙特卡洛算法。所以,他强调AlphaGo Zero以及AlphaZero不是所谓的“零知识”。这里的“零知识”只针对专有领域知识(domain knowledge),不包括像蒙特卡洛树搜索这样经过人类多年研究的算法。


最后,越说越激动的Marcus大声疾呼:“生活不是一场Kaggle竞赛!


“在生活中,没有东西是被整齐的预先包装好的(像Kaggle里的数据集那样),没有人能保证你昨天的挑战和今天的挑战一样,你希望学习的是可以重新使用的技能和知识,并且可以用在未来的挑战里,而实现这种可重用性才应该是大家关注的重点。”


除了有关AlphaZero的争论外,这场研讨会也提供了其他研究人工智能的思路。


来自加州伯克利大学心理学和哲学教授Alison Gopnik倡导将儿童的学习方法与人工智能相结合。Gopnik是第一位将概率模型应用于儿童学习的认知科学家,特别是使用因果贝叶斯网络框架。 在过去的15年中,她将计算模型应用于早期认知发展的许多领域,包括物理和社会概念的学习。


阿兰图灵在1950年就说过,“与其尝试模拟成人的大脑,为什么不直接创造一个模拟小孩的大脑。” Gopnik在研究中发现,一个四岁的小孩可以理性地从条件概率推断复杂的因果结构;在面对新证据后,会整合和推翻先前的知识;推断出未观察过的结构;推断出抽象的分层超假设;在物理、生物和心理学领域推断出理论知识。


近几年,越来越多的研究人员发现年级小的孩子更具有探索性。Gopnik总结了小孩子的学习特点,比如具有很强的求知欲而不是奖励机制,这些发现都对人工智能研究带来了一些新的方向和思考。


Interpretable Machine Learning :一场关于机器学习可解释性的辩论


从左到右:Patrice Simard,Kilian Weinberger,Yann LeCun

近年来,复杂的机器学习模型(如深度神经网络)在图像识别、语音感知、语言建模和信息检索等广泛的应用方面取得了出色的性能。人们对解释由这些模型习得的表征及决策的兴趣逐渐爆发出来,也催生了在可解释机器学习,因果关系,安全AI,社会科学,自动科学发现,人机交互(HCI),众包,机器教学和AI道德等方面的研究。这场可解释机器学习的研讨会的目的在于将这些密切相关但往往被孤立的主题联系在一起。


可解释的机器学习,使我们可以参考模型的预测结果,还有可能通过理解模型的结构更好地理解命题本身,例如犯罪预测及公共政策制定等;同时,理解模型本身又可以让我们可以建立更准确的预测模型;在自动驾驶等领域,我们需要超越现在的“黑箱模型”的可解释模型来避免罕见但代价惨重的错误。



NIPS可解释机器学习研讨会包含6个演讲,以及两场讨论,并公开宣布了一场可“解释挑战赛”。(https://community.fico.com/community/xml)。


上半场的演讲主要就如何进行可解释机器学习研究的方法展开,两位演讲者介绍了因果概率关系,以及一种结合物品检测和CNN图像特征,生成可理解的物品图像内容。


在可解释挑战赛公布之后进行的第一场小组讨论以十分平和的方式展开,Hanna Wallach, Kiri Wagstaff, Suchi Saria, Bolei Zhou和Zack Lipton 探讨了再进行可解释研究中常见的问题,以及需要注意的事项,有趣的是在讨论的最后,嘉宾们达成了一致,“明确需要进行解释的对象是谁”非常重要。


在 NIPS 2017第四日下午的可解释机器学习专题研讨会的最后一个小时,一场辩论点爆了现场的气氛,并吸引了大量的参会人员现场围观。毋庸置疑,这是第四天的Symposium中最火爆的一场。


这场NIPS 有史以来第一场辩论的主题为 “可解释性在机器学习中是必要的”(Interpretable is necessary in machine learning)。


正方一辩:微软研究院杰出工程师 Patrice Simard


正方二辩:微软研究院高级研究员 Rich Caruana


反方一辩:Facebook 人工智能研究部门(FAIR) 负责人 Yann LeCun


反方二辩:康奈尔大学副教授 Kilian Weinberger


本次辩论由正反方分别陈述各自观点拉开序幕:


正方一辩 Rich Caruana


Caruana举了一个例子:医院用深度神经网络来决定肺炎病人的住院顺序,死亡率高的入院。但是,模型通过某项数据发现,患哮喘的人肺炎的死亡率比较低,因为他们经常会去医院配药、做诊断等等,所以很多病情发现的早。那么按照这个模型的设定,同样患肺炎的人,患哮喘的人是不是应该排在队伍的后面。


Caruana想用这个例子说明,社会中很多基于数据的模型做决定,但往往没有正确地评估这个模型是否预测出正确的结果。解决的方法有,就是可解释模型。通过在可解释机器学习上的研究,他们发现不只是哮喘,心脏病也会降低肺炎病人的死亡率,道理是一样的。如果不解决可解释性,根本无法预料这些问题的存在。


反方一辩 Yann LeCun


LeCun的观点非常直接:世界上有这么多应用、网站,你每天用Facebook、Google的时候,你也没有想着要寻求它们背后的可解释性。人类的大脑有限,我们没有那么多脑容量去研究所有东西的可解释性。有些东西是需要解释的,比如法律,但大多数情况,它们没有你想象中那么重要。


LeCun也举了个例子:他多年前和一群经济学家做了一个模型预测房价。第一个用的简单的线性预测模型,经济学家也能解释清楚其中的原理;第二个用的是复杂的神经网络,但是效果比第一个好上不少。结果,这群经济学家想要开公司做了。你说他们会用哪一个?


LeCun表示,任何时候在这两种里做选择,所有人都会选择效果好的那一个。很多年里,我们不知道药物里有什么成分,我们一直在用。最后,LeCun认为,整个神经网络是黑箱的故事,它并不是黑箱,你可以看到所有的参数,你可以做灵敏度分析。我们常常被解释性所催眠,因为它让我们放心。


正方一辩 Patrice Simard


Simard的主要意思是说到机器学习,就应该有一个准确的定义。机器学习的作用是从坏的函数中找到好的函数,我们可以把这个好函数的集合称为假设空间(hypothesis space)。很多人觉得深度学习的假设空间是固定的,但假设空间可以变得非常非常大(因为我们可以定义无限多的问题)。在我们逐步学习了文字,学习语言,学习了各种各样的东西的过程中,假设空间也在不断增加,这种小的积累过程让学习变的更容易,这可以被称为结构可解释性。


Simard的观点是,不关心可解释性的人应该停止来NIPS大会解释最新的假设空间。 而关心解释性的人可以记录假设空间的进化,让学习变得可以被解释,变得更容易。


反方二辩 Kilian Weinberger


在现实中,可解释性并没有那么重要。人人都在用很复杂的东西,比如很少有人能够完全理解英特尔的i7芯片的工作原理,但大家都很自然地用,而且很好用。机器学习也是这样。 在一些情况下人们会需求可解释性:一个是需要了解数据,神经科学家和生物学家需要进行研究,但他们其实需要的是敏感性分析 (sensitivity analysis)。他们只是需要知道什么特征会如何影响输出结果。另一个是机器学习debugging,但这个并不会帮助人们理解机器学习是如何运作的。最后一个是可靠性(accountability)。不懂的人关心可解释性是因为信任度(trust),正如80年代时人们发现乘坐的是女飞行员驾驶的飞机时会下飞机。30年之后,人们也会觉得害怕自动驾驶汽车的人是很奇怪的。


Kilian问现场听众一个问题 —— 假设你要做心脏病手术:医生做手术有10%的死亡率;而手术机器人只有1%的死亡率。当医生出错时,医生可以理解自己犯了什么错而机器不会。这种情况下,是选让医生动手术还是手术机器人? 不出所料,大部分现场听众都选择了使用手术机器人。


在正反双方进行完观点陈述后,辩论进入第二个环节:正反双方互相答辩。双方就各自的观点进行了充分且辩证的讨论,双方探讨内容包括但不局限于,因果关系概率的重要性,测试的可靠性问题,结果可复制性问题等。


在辩论的最后一个环节,每位选手要求对对方辩题陈述一个自己认为最好的观点:


Yann LeCun:在机器拥有意识之前,机器会一直犯错。在对部署系统进行测试的时候尤其需要注意,不能忽略常识相关的测试场景。


Patrice Simard:过去神经网络不好用是因为数据不够多,但越来越多的数据会让模型的各种性能,包括可解释性也变得更好。


Kilian Weinberger:当人看到机器学习算法进行决策的时候,我们会用人的思维去理解机器;可解释性可以让我们理解机器的运转方式与人不一样。


Rich Caruana :人们介意的是准确度,而并不是很介意是否可以解释,可能只有科学家会为了完善理论而倾向可解释的模型。



Meta Learning:四大门派的观点冲突和Schmidhuber的骄傲:


Meta-learning 专题研讨会开始于对当前深度学习模型复杂度的探讨:超参数的调试与网络结构直接决定了训练的最终效果,但是这两部分的选择随着深度学习的发展变得愈加繁重。想象一下从相对简单的5层LeNet到异常复杂的GoogleNet,虽然模型的结果得到了令人惊讶的提升,但是其复杂的程度不再是几个工程师或是学者可以轻易接受的。Meta-learning正是对这样的关键问题进行的研究,尽管目前学界并未在Meata-learning的定义上达成一致,但是无论从何种角度出发最终的目标均是一致的:成熟的Meta-learning方法可以减轻工程师和学者在应用与研究过程中对模型调试的压力,从而将精力集中在解决主要问题上。本次研讨会从以下4个角度出发,对未来Meta-learning的发展进行探讨。


Evolving Optimization 主张学习算法的结构是最为重要的研究方向,因此这一流派认为对于任意的学习问题,主要的学习框架应由人来构建,而其余的任务则交给计算机来完成。事实上,当我们回顾所有成熟的工程问题时,我们会发现人们在解决这些问题时只需要将时间与精力投入在高层次的框架设计上,其中的细节均由计算机依照优化的目标迭代设计。为什么在机器学习的问题上我们还不能达到这样的程度呢?被广泛接受的说法是通用的机器学习算法的搜索空间巨大,在有限的时间与资源下难以寻找到最优的方法。正是针对这一问题,Evolving 流派认为从遗传的角度来寻找构建Meta-learning系统是合适的。


Bayesian Optimization 认为在学习过程中(Hyperparameters)对超参数的调整效率是极低的,且代价是极为昂贵的。考虑(Model Selection)模型选择的问题,研究者根据有限的数据反复尝试不同的参数组合去选择在当前数据集支撑下的最优模型,如果不考虑根据常识经验得到的通用设置,得到最优解的时间显然是随着超参数数目指数级增加的。通过 Bayesian Optimization 的方法,同时进行 exploration 和 exploitation,寻找最优参数的过程将有可能得到加速。值得一提的是,使用 Bayesian Optimizaiton 理念的 AutoNet 工具包是第一个在深度学习竞赛中获胜的 AutoML 工具包。


Gradient Descent 方面认为 Meta-learning 可以有三种方法实现: 1> Model Based, 2> Metric based, 3> Optimization Based。在目前的研究中,这三种方法并没有优劣之分,各有千秋。同时,优秀的学习被定义为成功地融合学习算法结构、优化学习所需要的数据以及优化方法的细节,缺少任一项学习的效果均会大打折扣。Meta-learning 应当被定义为从通用的学习目标开始,收到特定的(设计好的)影响后逐渐变为专精的学习过程。而真正的端到端学习则是机器具有能力计算并学习任意的模型,从而有目的的解决不同问题。


Reinforcement Learning 认为在学习中要解决特定的问题,则必须在学习之前获得足够正确的先验知识。这一点明显的反映在奖励函数(Reward Function)的设计上,一旦奖励函数被正确的设计,整个学习过程并不需要过多的人为干预。于是,能否正确的建立关于学习的模型成为了Meta-learning的关键。设计奖励函数的做法,往往也被称为引入inductive bias,即将人为的经验加入到机器的学习过程中从而加速学习正确目标的过程。然而,这种做法的正确性也得到了讨论:往往人为设计的奖励函数仅考虑到与目标的契合,而忽略了与agent行为的一致性。这种失配被称为Preferences-Parameters Confound。


显然,这四个角度对于Meta-learning的理解各有侧重,甚至存在不少冲突的观点。在 Juergen Schmidhuber 教授的演讲中,他针对真正的端到端学习给出了自己的定义。而在接下来 Satinder Singh 教授的演讲中,Singh教授直言不讳,划掉了PPT中的Meta-learning字样:看过刚刚Schmidhuber 教授的演讲,我自觉不足以在这里讨论Meta-learning的话题,接下来只侧重Reinforcement Learning。由此可见对于这样仍然未被完全定义的领域中,不同的学者对于Meta-learning的看法有多么大的分歧。当然,正是这样的分歧与争论不断推动着 Meta-learning 向前发展。



Juergen Schmidhuber


Deep Reinforcement Learning - DRL的泛化之路


自从AlphaGo在2016年战胜了诸如李世乭和柯洁后,深度强化学习受到了越来越多的关注。本次NIPS大会期间,DeepMind公布的AlphaZero更是让深度强化学习的分享备受期待。


第一位演讲者是来自Google DeepMind的David Silver,演讲题目“AlphaZero - 不用人类知识来驾驭棋类比赛”。David Silver是伦敦大学学院的计算机科学教授,目前在Google DeepMind任职,是Alpha系列程序的核心研究者。


在演讲中,他首先介绍了围棋的特点,强调围棋比起其他棋类可以有更多的变化。然后话锋一转进入演讲的主角Alpha系列的发展。作为第一个击败人类世界冠军的程序,AlphaGo包含两个不同的神经网络:策略网络和价值网络,再结合著名的蒙特卡洛树搜索来完成训练。同AlphaGo相比,它的迭代版本AlphaGo Zero采用了第一法则学习原理。该方法相比之前主要有如下四个特点:1. 无人类数据;2. 无人工特征; 3. 单独的神经网络; 4.简单搜索。可以看出比起上一代AlphaGo Zero有了相当程度的简化,此时演讲者指出了他的重要观点:越简单,越广泛(Less complexity → More generality)。接着Silver介绍了AlphaGo Zero的两种新的神经网络,其中策略网络用来预测围棋如何下子,而价值网络负责预测获胜者,二者的合成被应用在ResNet上。至于新的搜索方法,在AlphaGo中主要采用Search-based policy improvement和Search-based policy evaluation两个角度来完成强化学中的Search-based policy迭代。


在对原理有了大概的介绍后,Silver对比了AlphaGo Zero与之前版本的性能对比。其中AlphaGo Zero三天可以超过AlphaGo Lee,21天可以超过AlphaGo Master,在40天的训练后就可以击败世界冠军。在训练过程中研究者发现一个有趣的现象,AlphaGo好像逐渐学会了一些特定棋谱,并热衷于把它们应用于比赛。


接下来,最新的版本AlphaZero让棋类比赛更加简单。AlphaZero通过200到400次不等的迭代就已经可以分别在国际象棋(Chess),将棋(Shogi),和围棋(Go)中击败现有最强的棋类算法。其中象棋需要4小时,将棋需要2小时,击败AlphaGo Lee仅仅需要8小时训练。AlphaZero的具体信息虽然这次并没有透露,但其强大的能力让我们对深度强化学习多了一份期待。最后演讲者再次点题,坚持简单的算法可以应用与更多的场景。


之后的提问环节,有观众对完全没有人类知识表示不解。这里Silver给出解释,在模型的输入信息中除了包含规则外还有一定很简单的围棋的输入和输出范例,但数量很少,而且非常初级。



第二个重要演讲者Joelle Pineau,是来自麦吉尔大学计算机科学系的教授。她的主要研究方向集中在“Planning, learning and decision-making,mobile robotics, human-robot interaction” 和 “adaptive treatment design。”


这次她的研究是关于学术研究的心病 - 论文复现。由于研究者操作手段不同,信息不对称等,对结果复现造成了很大的难度。这个问题严重困扰科学研究,让成果验证更难。根据自然杂志的调查结果,在1576名受试者中,有52%的人认为这是一个严重的危机,38%的人相信这有影响。在另一项调查中,大部分科研工作者都经历过无法复现别人成果的痛苦。其中化学领域超过80%,生物领域达到77%,物理和工程相关领域则有近70%。在我们关注的机器学习领域,这种现象同样达到了80%。所以演讲者希望有一个统一的平台来进行强化学习的研究。通过这个平台,研究者可以在上面调用已有的标准的底层算法,利用统一的硬件配置和算力支撑,来让强化学习的研究更加可控,对模型参数的调整也会更容易。


虽然研究者的要求千差万别,但最底层的需求实际上存在大量重复。如果统一该过程,并且对操作结果进行详细描述,那会使人工智能研究成果更容易验证,大大降低了偶然性和噪音。这种平台的推广无疑可以加速人工智能的发展,也是现在各个学科的一个发展方向。


接着作者介绍了两个易混淆的概念:Reproducibiity和Replication。其中Replication只是简单的重做实验,在需要相同的数据,达到同样结果时需要,这种情况只适合与模拟数据,在现实生活中往往很难达到。Reproducibillity就复杂的多,从精确的数据,参数的调整,清晰的文章和代码,计算资源,系统配置等都需要达到一定要求,才可能把原有文章的结果应用到新的地方。


最后,演讲者呼吁我们都能投身于一个ICLR 2018 Reproducibility Challenge的活动,互相监督验证,共同验证已有的文章结果,来促进人工智能技术的进一步发展。


接下来的亮点在于来自卡内基梅隆大学的教授Ruslan Salakhutdinov的精彩分享:神经地图-深度强化学习的结构化记忆。


Ruslan Salakutdinov是最近在机器学习领域很活跃的年轻教授,主要研究方向包括Deep Learning, Probabilistic Graphical Models, 和Large-scale Optimization。这次他为我们带来了如何给强化学习植入记忆,让其在不同环境下都可以表现优异。


演讲从监督学习开始,认为大部分深度学习都可以表示为监督学习:映射并输出一个结果(Mapping and input to an output)。接着演讲谈到了环境对强化学习的影响,主要体现在三个方面:1. 环境是随着时间动态变化的;2. 动作对环境的影响存在不确定的滞后性;3. 对环境的标注是成本高昂而且很难实现的。 为了解决这些潜在的问题,Ruslan根据前人的工作,引入了记忆的概念来调整agent,使得物体进入新环境时候有更好的表现。但外在记忆的引入又引起了新的问题,比如效率较低(因为要记录所有的信息)。作者的解决方案是利用位置感知记忆(Location Aware Memory),这种方法可以起到一种类似于“地图”的效果,帮助agent进行探索。而且该方法在输出结果时候是利用稀疏的结果作为输出,可以防止agent过多的重写记忆。之后演讲者又详细介绍了这种神经网络的具体细节,比如operations, global read, context read和write。并且举了不同的迷宫例子,还可以应用与定位问题和自然语言理解问题。在演示的环境,可以清晰看出之前有过“记忆”的agent进入新模型时候往往会根据经验有更好的表现。


这种方法的理想状态,agent会拥有读写自己外在记忆的能力,而外在记忆会和知识库互相转化,并且agent也可以用不同的方法来和知识库进行理性的交流(reason communication)。最后演讲者还提出了一些展望,他表示希望进行不同agents共享记忆进行交流的尝试。


之后的提问环节,有听众询问是否可以构造更高级更抽象的模型?演讲者给出了肯定的答案,但也承认在现阶段距离该目标还很远。在一个开放的环境(open domain)会遇到更多的困难。另一位听众问到是否可以尝试不同的环境,比如新环境和旧环境有较大差别的情况?有过尝试但目前环境的差别并不是全方位的,演讲者希望有更多更好的模拟器出现。


这次的DRL分享反应出研究方向主要集中在深度强化学习的泛化和性能的提升。DeepMind采用了简化算法的方法,而Ruslan则选择了引入记忆的途径。殊途同归,但该方向取得的进展无疑是激动人心的。







没有更多推荐了,返回首页