2017-10-15 23:20:45 mystery_guest 阅读数 326
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5905 人正在学习 去看看 杨波
第一步:采样
人的声音频率是有范围的,所以采样也应当选取范围。采样率是最低频率的2倍,此乃常识。至于最低频率怎样定,可以根据实际情况。
1、注意你的麦克风,有些麦克风是有接受范围的,把频率定得比麦克风的能力极限还低,显然是浪费。
2、用途。如果是要求可靠性很高的系统,则要注意加大频率范围。如果要求实时性比较高,那就要考虑处理机的速度了,数据量和频宽是正比的。
3、性别。男人的低音部分比女人更重要。所以如果对性别加以区分,那么在识别不同性别时应该采用不同下限。
4、特别指出,小心干扰。频率范围大了,噪音也就多。如果扩大的范围内噪音比较多,那还是不要的好。
第二步:语音处理
采样进入计算机后,成为一段段的数据,表明各个时刻的声音能量。处理时,第一步就是消除噪音,消不了这个东西,下面怎么都不好搞。可消除的主要是电器噪音,方法是完全静音时,录下电路信号,反相回到声音上去就成了。再下面就是断字了。人类语言是分音节的,音节之间是有一个停顿的,特别是汉语就更明显了。我主要讲的是汉语,在输入的数据中舍去能量低于一定值的部分,余下次的一段段的就对应各个字了。这需要解决两个问题,第一,舍去的标准,显然人说话声音有高有低,不能硬来。我的建议是取波形,不管说话声音高低,声音总是有形状的,不妨先取几个完整的波形,算出平均能量,取它的几分之一作为标准即可。而且这个过程似乎应该是动态的。第二是舍去一部分后,余下的部分可能不太整齐,由于噪音等原因,该舍的可能没舍,这就会有问题。解决这个问题大概可以在舍去时,不仅根据值的大小,还要根据两边的情况。这关系到舍去函数了,不是很难。断开各个字之后,就可以进行匹配了。原来的样本经过处理后,变成时间的函数,以时间为顺序取得一组值,成为N维向量。待处理的数据也同样进行,采用波形插值法从采样中取出N维向量,与样本进行匹配。 这关键是一个匹配函数的问题,即由两个向量在空间的相互位置得到一个匹配度,与样本依次比较,得出各自的匹配度。
由于各个样本都要匹配,所以速度很重要。通常的改进方法是进行多次匹配,即只把某些坐标进行对比,值大于某个标准才进行二次匹配。 这个标准可以是动态的,比如已进行过的对比的第M大的值。 为什么不到最大值,主要是需要保留多个结果。同时M可调,也便于在处理特殊情况时进行智能应变。匹配时,相应的计算函数相当重要,样本要相当的标准,同时以此样本为中心,扩张出多层区域,每层内的向量得到各层的匹配度,由此只要划定相应的匹配区域,给出值就可以了。
这里要多说一点,一般来说,匹配时都采用统一的函数,我却不这么认为,特别是汉语,语音的种类很少,不如将它们分组,每个组函数都不一样。也就是在匹配时,跟据要操作的目标,可以得到相应的匹配函数,进行匹配。 我觉得这要比用同一个匹配函数有效的多。而且,在调节对具体某个字的识别时,也很容易,同时在让它自动学习的时候,也很容易搞。你可以自己设计一个软件,把样本显示在二维坐标上(X:时间,Y:能量),输入一个声音时,把相应的量也显示在坐标系上。这是两条曲线。一方面,你可以反复地读样本,让软件把各条曲线求出来,然后取个中间值更新样本。另一方面,你也可以人脑辅助,根据自己反复读样本得出的曲线,对坐标空间进行分割,得出相应的匹配函数。也就是说,样本可以通过学习自动升级,匹配函数也可以由人脑加入进行升级。我认为这样会更高效一点。分组还有一个好处就是把相近的音归到一组,如果输入的数据和其中一个相差太远,其他的就可以跳过去了。这样可以提高速度。匹配这后,就把结果送入下一模块,其结果应该是这样:X:概率Xo,Y:概率Yo……如此等等,要送入概率最大的M个字,同时附上各字的概率。
第三步:上下文对照选定文字
这似乎和词库有关,所以词库的组织要好,词汇量要大,重要的一点是自己能够学习。出现新词要能记住,常用词要靠前,等等。这是微软的做法。微软采取的另一个方法是输入的词语先不交给应用程序,而是自己有一个缓冲区,输入的词语放在缓冲内,显示在屏幕上,这样一方面可以在输入下文后,依次修改上文,(交给应用程序后可就改不了。)另一方面也可以让用户通过手工操作对文字选择进行干预,从而提高效率。
2019-05-06 15:59:00 cjb100 阅读数 18
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5905 人正在学习 去看看 杨波

arduino 语音音箱 :语音控制、MP3播放、报时、回复温湿度情况

效果图

线路图

包装后的效果

功能

需要材料

  1. arduino板
  2. MP3播放模块及喇叭
  3. 时钟模块
  4. 温湿度模块
  5. 语音识别模块
  6. 面包板及其他线材、电阻
  7. TF卡(用于储存音频)

原理

  1. 通过语音识别模块对使用者的语音指令进行与设置的指令匹配,匹配成功调用MP3模块播放对应音乐。
  2. 因为MP3播放需要一定的delay,但是由于系统自带的delay会阻塞arduino的操作,因此需要将大的延迟拆分成许多小块,每执行一个小块,就调用一次语音识别模块,使其能在有指令后能快速反应。
  3. MP3模块的使用需要按指定格式,例如文件名需要按照指定格式命名。中间有部分音频做为音箱的人机交互的对话。

理论知识

Arduino是一个基于开放原始码的软硬件平台,可用来开发独立运作、并具互动性的电子产品,也可以开发与PC相连的周边装置,同时能在运行时与PC上的软件进行交互。为了测量正弦波电压有效值,首先我们设计了单电源供电的半波整流电路,并进行整流滤波输出,然后选择了通过Arduino设计了读取电压有效值的程序,并实现使用此最小系统来测量和显示电压有效值。在频率和直流电压幅度限定在小范围的情況下,最小系统的示数基本和毫伏表测量的值相同。根据交流电压有效值的定义,运用集成运放和设计的Arduino最小系统的结合,实现了运用少量元器件对交流电压有效值的测量。

实验目的

在很多人眼里依旧是新潮玩意的智能音箱,往往具备智能家居声控中心和个人助理两大核心功能。通过智能音箱可以实现对家居中的智能电器进行联网和语音控制,同样也可完成日常安排、订餐厅、叫车、阅读、翻译、通话等类似个人助理的功能。智能音箱的优势在于其能够通过语音实现“隔空操作”,在人们不方便使用手机或其他电子设备时提供帮助。

智能音箱首先承载的功能是音乐播放,其次是联网智能,因此,音乐娱乐市场的繁荣程度是音箱普及的前提条件。

通过智能音箱可以控制智能灯泡、窗帘、安全监控等智能家居产品,因此未来智能音箱的普及将大大提升家庭语音交互的使用频率,从而带动智能家居行业进入实质性发展阶段。

主要功能

1、开机语音:欢迎使用语音音箱;

2、询问温湿度情况:她会及时做出反应,播报当前温度和湿度;

3、命令随机:会随机播放任何一首音乐,TF里共存储20首音乐;

4、命令暂停:暂停当前正在放的音乐;

5、命令继续,继续播放已暂停的歌;

6、加大/降低音量:在播放过程中,可以用语音控制加大或者调低音量;

7、上一首/下一首:切换前一首或下一首歌曲。

前期准备知识

C语言

Arduino使用C/C++编写程序,虽然C++兼容C语言,但这是两种语言,C语言是一种面向过程的编程语言,C++是一种面向对象的编程语言。早期的Arduino核心库使用C语言编写,后来引进了面向对象的思想,目前最新的Arduino核心库采用C与C++混合编写而成。

通常我们说的Arduino语言,是指Arduino核心库文件提供的各种应用程序编程接口(Application Programming Interface,简称API)的集合。这些API是对更底层的单片机支持库进行二次封装所形成的。例如,使用AVR单片机的Arduino的核心库是对AVR-Libc(基于GCC的AVR支持库)的二次封装。

传统开发方式中,你需要厘清每个寄存器的意义及之间的关系,然后通过配置多个寄存器来达到目的。

代码

//引入依赖
#include <LiquidCrystal.h>
#include <ld3320.h>

#include <stdlib.h>

#include <SoftwareSerial.h>    //软串口头文件名
#include <DFPlayer_Mini_Mp3.h> //MP3头文件名

#include <Wire.h>
#include <RTClib.h>
#include <DHT.h>
#include <DHT_U.h>

#define DHTPIN 14
#define DHTTYPE DHT11
#define Led A1
#define MP3GO A3

void showTimeAndDHT();
void play();
void defa();
int recoderRun();
int myDelay(int t);
int random_play();
int getRandom();

RTC_DS1307 RTC;
VoiceRecognition Voice;
DHT dht(DHTPIN, DHTTYPE);

byte heart[8] = {0b00100, 0b01000, 0b11000, 0b11111, 0b00011, 0b00010, 0b00100, 0b00000};
int theVo = 25;

// 初始化针脚
const int rs = 3, en = 5, d4 = 6, d5 = 7, d6 = 8, d7 = 10;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

int myDelay(int t)
{
  int c = 0;
  c = t / 100;
  for (int i = 0; i < c; i++)
  {
    delay(100);
    if (!recoderRun())
      return 0;
  }
  return 1;
}

int getRandom()
{
  int i = 30;
  randomSeed(RTC.now().second());
  i = random(53);
  while ((i >= 0 && i <= 10) || (i >= 21 && i <= 29))
  {
    i = random(53);
  }
  return i;
}

int random_play()
{
  int i = 30;
  i = getRandom();
  mp3_play(i);
  // if (!myDelay(300000))
  //   return 0;
  delay(5000);
  while(1)
  {
    if(!myDelay(1500)) return 0;
    if(digitalRead(MP3GO) == HIGH)
    {
      i = getRandom();
      mp3_play(i);
    }
  }
  return 1;
}

void setup()
{

  // 设定LCD 的行列数目(2 x 16)
  pinMode(MP3GO, INPUT);

  lcd.begin(16, 2);
  Serial.begin(9600);
  Wire.begin();
  RTC.begin();
  RTC.adjust(DateTime(__DATE__, __TIME__));

  lcd.createChar(1, heart);
  lcd.write(1);
  //Serial.println(F("DHTxx test!"));
  dht.begin();

  Serial.begin(9600);     //串口波特率,9600
  mp3_set_serial(Serial); //定义MP3串口为Arduino串口。
  mp3_set_volume(theVo);  //设置音量。

  Voice.init();                     //初始化VoiceRecognition模块
  Voice.addCommand("kai deng", 0);  //添加指令,参数(指令内容,指令标签(可重复))
  Voice.addCommand("guan deng", 1); //添加指令,参数(指令内容,指令标签(可重复))
  Voice.addCommand("xia yi shou", 2);
  Voice.addCommand("shang yi shou", 3);
  Voice.addCommand("zhan ting", 4);
  Voice.addCommand("ji xu", 5);
  Voice.addCommand("ting zhi", 6);
  Voice.addCommand("sui ji yin yue", 7);
  Voice.addCommand("xian zai shi jian", 8);
  Voice.addCommand("wen shi du qing kuang", 9);
  Voice.addCommand("xian zai wen shi du", 10);
  Voice.addCommand("jia da yin liang", 11);
  Voice.addCommand("jiang di yin liang", 12);
  Voice.addCommand("bo fang yin yue ba", 13);
  Voice.addCommand("guan ji",14);

  Voice.start(); //开始识别

  pinMode(Led, OUTPUT);   //初始化LED引脚为输出模式
  digitalWrite(Led, LOW); //LED引脚低电平

  //showTimeAndDHT();
  defa();
  play();
}

void showTimeAndDHT()
{
  lcd.setCursor(0, 0);
  lcd.write(1);
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  float hic = dht.computeHeatIndex(t, h, false);
  lcd.print('T');
  lcd.print(':');
  lcd.print(hic);
  lcd.print(' ');
  lcd.print('H');
  lcd.print(':');
  lcd.print(h);
  lcd.print(' ');
  lcd.setCursor(0, 1);
  DateTime now = RTC.now();
  lcd.print(now.year());
  lcd.print('/');
  lcd.print(now.month());
  lcd.print('/');
  lcd.print(now.day());
  lcd.print(' ');
  lcd.print(now.hour());
  lcd.print(':');
  lcd.print(now.minute());
}

void play()
{
  mp3_play(23);
  delay(2000);
}

int whatTH()
{
  int h = 0, m = 0, hic = 0, t = 0;
  if (!myDelay(1000))
    return 0;
  mp3_stop();
  showTimeAndDHT();
  t = dht.readTemperature();
  h = (int)dht.readHumidity();
  hic = (int)dht.computeHeatIndex(t, h, false);
  mp3_stop();
  mp3_play(26);
  if (!myDelay(2000))
    return 0;
  if (t / 10 > 0)
  {
    mp3_play(t / 10);
    if (!myDelay(600))
      return 0;
    mp3_play(10);
    if (!myDelay(600))
      return 0;
  }
  if (t % 10 != 0)
  {
    mp3_play(t % 10);
    if (!myDelay(600))
      return 0;
  }
  if (t == 0)
  {
    mp3_play(2);
    if (!myDelay(600))
      return 0;
    mp3_play(10);
    if (!myDelay(600))
      return 0;
    mp3_play(4);
    if (!myDelay(600))
      return 0;
  }
  if (!myDelay(1000))
    return 0;
  mp3_play(24);
  myDelay(800);
  if (h / 10 > 0)
  {
    mp3_play(h / 10);
    if (!myDelay(600))
      return 0;
    mp3_play(10);
    if (!myDelay(600))
      return 0;
  }
  if (h % 10 != 0)
  {
    mp3_play(h % 10);
    if (!myDelay(600))
      return 0;
  }
  mp3_stop();
  defa();
  Voice.read();
  return 1;
};

int whatTime()
{
  int h = 0, m = 0, hic = 0, t = 0;
  showTimeAndDHT();
  mp3_stop();
  if (!myDelay(1000))
    return 0;
  mp3_play(25);
  if (!myDelay(2000))
    return 0;
  h = (int)RTC.now().hour();
  if (h / 10 > 0)
  {
    mp3_play(h / 10);
    if (!myDelay(600))
      return 0;
    mp3_play(10);
    if (!myDelay(600))
      return 0;
  }
  if (h % 10 != 0)
  {
    mp3_play(h % 10);
    if (!myDelay(600))
      return 0;
  }
  if (h == 0)
  {
    mp3_play(2);
    if (!myDelay(600))
      return 0;
    mp3_play(10);
    if (!myDelay(600))
      return 0;
    mp3_play(4);
    if (!myDelay(600))
      return 0;
  }
  mp3_play(21);
  if (!myDelay(600))
    return 0;
  m = (int)RTC.now().minute();
  if (m / 10 > 0)
  {
    mp3_play(m / 10);
    if (!myDelay(600))
      return 0;
    mp3_play(10);
    if (!myDelay(600))
      return 0;
  }
  if (m % 10 != 0)
  {
    mp3_play(m % 10);
    if (!myDelay(600))
      return 0;
  }
  if (m != 0)
  {
    mp3_play(22);
    if (!myDelay(600))
      return 0;
  }
  mp3_stop();
  defa();
  Voice.read();
  return 1;
}

int recoderRun()
{
  int h = 0, m = 0, hic = 0, t = 0;
  switch (Voice.read()) //判断识别
  {
  case 0: //若是指令“kai deng”
    //digitalWrite(Led, HIGH);                //点亮LED
    break;
  case 1: //若是指令“guan deng”
    //digitalWrite(Led, LOW); //熄灭LED
    break;
  case 2:
    //mp3_stop ();
    mp3_play(28);
    delay(2500);
    Voice.read();
    if (!random_play())
      return 0;
    break;
  case 3:
    //mp3_stop ();
    mp3_prev();
    break;
  case 4:
    mp3_pause();
    break;
  case 5:
    mp3_play();
    //mp3_random_play();
    break;
  case 6:
    mp3_stop();
    break;
  case 13:
  case 7:
    mp3_play(29);
    delay(2500);
    Voice.read();
    if (!random_play())
      return 0;
    break;
  case 8:
    whatTime();
    defa();
    break;
  case 9:
  case 10:
    whatTH();
    defa();
    break;
  case 11:
    theVo += 2;
    mp3_set_volume(theVo);
    break;
  case 12:
    theVo -= 2;
    mp3_set_volume(theVo);
    break;
  case 14:
    Voice.reset();
  default:
    return 1;
    break;
  }
  delay(100);
  return 0;
}

void defa()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.write(1);
  lcd.print("Intellgent MP3");
  lcd.setCursor(0, 1);
  lcd.print("la la la ~");
}

void loop()
{
  recoderRun();
}

参考链接

【1】Arduino连接LCD1602显示屏

【2】Arduino使用蓝牙通信模块

【3】使用Arduino Uno输出自定义频率的PWM

【4】LD3320 Board

【5】Arduino系列教程十:语音识别模块 LD3320

【6】MP3音乐播放器模块 示例教程 适用于Arduino平台

【7】DFPlayer Mini模块

2019-07-06 20:02:12 weixin_44611644 阅读数 193
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5905 人正在学习 去看看 杨波

分享一下自己做这个小程序的笔记!

使用工具:PyCharm,python版本3.7,opencv3
使用的包:
import
原理和功能
使用opencv3调用摄像头,获取图像保存,对保存的图片进行OPENCVLBHP算法训练,利用训练集和分类器实现人脸识别。使用语音模块进行人机交互,使用数据库相关操作保存和读取数据库!

详细设计
由于保存人脸信息和训练集需要文件夹,这里先进行检测环境,如果没有就进行创建。

def makeDir(engine,x):
    if not os.path.exists("face_trainer"):
        print("创建预训练环境")
        engine.say('检测到第一次启动,正在创建预训练环境')
        os.mkdir("face_trainer")
        engine.say('创建成功')
        engine.runAndWait()
        x=2
    if not os.path.exists("Facedata"):
        print("创建训练环境")
        engine.say('正在创建训练环境')
        os.mkdir("Facedata")
        engine.say('创建成功')
        engine.runAndWait()
        x=2
    return x

通过摄像头采集人脸信息

def getFace(cap,face_id):
    face_detector = cv2.CascadeClassifier('./lib/haarcascade_frontalface_default.xml')
    print('\n Initializing face capture. Look at the camera and wait ...')
    count = 0
    while True:
        sucess, img = cap.read()
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = face_detector.detectMultiScale(gray, 1.3, 5)
        for (x, y, w, h) in faces:
            cv2.rectangle(img, (x, y), (x+w, y+w), (255, 0, 0))
            count += 1
            cv2.imwrite("Facedata/User." + str(face_id) + '.' + str(count) + '.jpg', gray[y: y + h, x: x + w])
            cv2.imshow('image', img)
        # 保持画面的持续。
        k = cv2.waitKey(1)
        if k == 27:   # 通过esc键退出摄像
            break
        elif count >= 1000:  # 得到1000个样本后退出摄像
            break
    cv2.destroyAllWindows()

cv2.CascadeClassifier中加载分类器,使用官方给定的xml分类器进行识别。

接着后面会用到路径有关的读取函数实现如下:

def getImagesAndLabels(path,detector):
        imagePaths = [os.path.join(path, f) for f in os.listdir(path)]
        faceSamples = []
        ids = []
        for imagePath in imagePaths:
            PIL_img = Image.open(imagePath).convert('L')
            img_numpy = np.array(PIL_img, 'uint8')
            id = int(os.path.split(imagePath)[-1].split(".")[1])
            faces = detector.detectMultiScale(img_numpy)
            for (x, y, w, h) in faces:
                faceSamples.append(img_numpy[y:y + h, x: x + w])
                ids.append(id)
        return faceSamples, ids

接下来是对已保存的人脸数据进行训练,生成属于我们自己的训练集 .yml文件

def trainFace():
    # 人脸数据路径
    path = 'Facedata'
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    detector = cv2.CascadeClassifier("./lib/haarcascade_frontalface_default.xml")
    print('Training faces. It will take a few seconds. Wait ...')
    faces, ids = getImagesAndLabels(path,detector)
    recognizer.train(faces, np.array(ids))
    recognizer.write(r'face_trainer\trainer.yml')
    print("{0} faces trained. Exiting Program".format(len(np.unique(ids))))

最后是实现人脸的识别模块

def checkFace(cam,names,engine):

    recognizer = cv2.face.LBPHFaceRecognizer_create()
    recognizer.read('face_trainer/trainer.yml')
    cascadePath = "./lib/haarcascade_frontalface_default.xml"
    faceCascade = cv2.CascadeClassifier(cascadePath)
    font = cv2.FONT_HERSHEY_SIMPLEX
    minW = 0.1 * cam.get(3)
    minH = 0.1 * cam.get(4)
    while True:
        ret, img = cam.read()
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = faceCascade.detectMultiScale(
            gray,
            scaleFactor=1.2,
            minNeighbors=5,
            minSize=(int(minW), int(minH))
        )
        for (x, y, w, h) in faces:
            cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
            idnum, confidence = recognizer.predict(gray[y:y + h, x:x + w])
            print(idnum)
            if confidence < 100:
                idnum = names[idnum]
                confidence = "{0}%".format(round(100 - confidence))
                say(engine, "欢迎      " + idnum + "签到成功!")
                cv2.putText(img, str(idnum), (x + 5, y - 5), font, 1, (0, 0, 255), 1)
                cv2.putText(img, str(confidence), (x + 5, y + h - 5), font, 1, (0, 0, 0), 1)
                cv2.imshow('camera', img)
                time.sleep(2)
                Insert_Data_Check(conn, cursor, idnum)#签到信息插入数据库
                return
            else:
                idnum = "unknown"
                confidence = "{0}%".format(round(100 - confidence))
                cv2.putText(img, str(idnum), (x + 5, y - 5), font, 1, (0, 0, 255), 1)
                cv2.putText(img, str(confidence), (x + 5, y + h - 5), font, 1, (0, 0, 0), 1)
        cv2.imshow('camera', img)
        k = cv2.waitKey(10)
        if k == 27:
            break

本系统中信息的保存和读取是从数据库中保存和获得的
用到的函数如下:
def_mysql
其中包含了数据库的连接和关闭以及初始化函数,表的检测和创建函数,插入和查询函数。重点在两个query函数,首先保存的姓名信息以列表的形式传给names列表,在进行识别签到时id的读取也是从这里读取的,接着是签到记录的查询函数,可以给定名字查询也可以给日期查询当天的所有人记录,也可以直接enter都按默认查询全部的签到记录。
主要代码如下

def Query_Data_Personal(conn, cursor):
    sql = "select name from personal"
    cursor.execute(sql)
    conn.commit()
    results = cursor.fetchall()
    list_name = []
    for x in results:
       x = list(x)
       list_name += x
    return list_name
def Query_Data_Check(conn, cursor, name='', *dates):
    if dates is ():
        if name == '':
            sql = "select * from check_table;"
        else:
            sql = "select * from check_table where name = '%s';" % (name,)
        print(sql)
        cursor.execute(sql)
    else:
        if name == '':
            for date in dates:
                sql = """select * from check_table where  check_time = any (
                    select check_time from check_table where DATEDIFF(date(check_time),'%s') < 1)
                    """ % (date,)
                print(sql)
                # 执行sql语句
                cursor.execute(sql)
        else:
            for date in dates:
                print(date)
                sql = """select * from check_table where name = '%s' and check_time = any (
                       select check_time from check_table where  DATEDIFF(date(check_time),'%s') < 1);
                       """ % (name, date)
                print(sql)
                # 执行sql语句
                cursor.execute(sql)
    # 提交到数据库执行
    conn.commit()
    results = cursor.fetchall()
    for x in results:
        print("("+ str(x[0])+",  "+x[1] +",  "+ str(x[2]) +")")

语音模块在py文件的开始进行导入,我这里选择import pyttsx3
对语音模块进行设置和使用如下

def say(engine,str):
    engine.say(str)
    engine.runAndWait()
    
`engine = pyttsx3.init()
    rate = engine.getProperty('rate')
    engine.setProperty('rate', rate - 20)
    say(engine, "欢迎使用人脸识别签到系统")``

后续直接使用say()可实现语音播放功能,上述代码的功能是实现语音播放等待和调整语速。

整个程序的主函数流程为,检测环境与初始化→录入人脸信息→对人脸信息进行训练→实现人脸识别签到功能。其中如果是第一次创建环境则不需要询问是否创建新的人脸,直接执行人脸录入模块。签到功能之后会跳转主菜单选择退出,查询签到记录,或者录入新的人脸。主函数代码如下

if __name__ == '__main__':
    engine = pyttsx3.init()
    rate = engine.getProperty('rate')
    engine.setProperty('rate', rate - 20)
    say(engine, "欢迎使用人脸识别签到系统")
    simply_0=1#标志变量 象征是否第一次使用系统
    simply_0=makeDir(engine,simply_0)
    say(engine, "正在初始化数据库")
    conn, cursor = Mysql_Init()
    Check_Tables(conn, cursor)
    say(engine, "数据库初始化完毕")
    names = []#打印已存在的人员姓名
    names = Query_Data_Personal(conn, cursor)
    print(names)
    while True:
        if simply_0 == 1:#不是第一次使用
            say(engine, "输入0录入新的人脸信息 输入其他跳转至人脸签到 ")
            value = input("0-录入 or other-签到模块")
            if value == '0':
                say(engine, "请输入您的姓名")
                name = input("请输入姓名:")
                names.append(name)
                # 新成员信息已保存到数据库中
                Insert_Data_Personal(conn, cursor, name)
                say(engine, "新的人员信息已保存到数据库中")
                say(engine,"正在打开摄像头")
                cam = cv2.VideoCapture(0)
                say(engine, "注视摄像头,开始采集人脸数据")
                getFace(cam,len(names)-1)
                say(engine, "采集完毕,开始训练")
                trainFace()
                say(engine, "训练完毕,跳转到人脸签到模块")
            else:
                say(engine, "正在打开摄像头")
                cam = cv2.VideoCapture(0)

        else:#是第一次使用系统
            say(engine, "这是首次录入人脸信息,请输入您的姓名 ")
            name = input("请输入姓名:")
            names.append(name)
            #新成员信息保存到数据库中
            Insert_Data_Personal(conn, cursor, name)
            say(engine, "新的人员信息已保存到数据库中")
            say(engine, "正在打开摄像头")
            cam = cv2.VideoCapture(0)
            say(engine, "注视摄像头,开始采集人脸数据")
            getFace(cam, len(names) - 1)
            say(engine, "采集完毕,开始训练")
            trainFace()
            say(engine, "训练完毕,跳转至人脸签到")

        say(engine, "输入0进行人脸签到,输入其他跳转至主菜单")
        simply_1 = input("输入0进行人脸签到,输入其他跳转至主菜单")
        if simply_1=='0':
            checkFace(cam, names, engine)
            say(engine, "签到记录信息已保存到数据库中")
            cam.release()
            cv2.destroyAllWindows()
        else:
            cam.release()
            cv2.destroyAllWindows()

        say(engine, "输入 0 退出系统 ,输入1 查询签到记录 其他任意键 录入人脸模块")
        key = input("输入key:(0 - 退出系统 ,1-查询签到记录 other - 重新启动系统)")
        if key=='1':
            say(engine, "输入要查询记录的姓名或日期")
            name_0 = input("输入要查询记录的名字")
            date_0=input("输入要查询记录的日期")
            Query_Data_Check(conn, cursor, name_0, *date_0)
            say(engine, "签到记录查询完毕")
            say(engine, "系统即将跳转至录入人脸模块")
        if key == '0':
            say(engine, "系统将退出,欢迎下次使用")
            dataBase_Close(conn)
            sys.exit(0)

运行的主要结果
初次运行系统,将自动创建训练环境以及数据库表
在这里插入图片描述
在这里插入图片描述
运行py文件,首先会打印数据库中已经保存的训练过的人脸信息姓名

在这里插入图片描述
因为已经录入过很多信息了,直接选择签到功能!
在这里插入图片描述
语音提示注视摄像头,签到成功后会弹出签到快照停留三秒,显示签到人员以及识别相似度。语音播放xxx签到成功!
在这里插入图片描述

如果选择查询签到记录,这里默认都不输入数据,查询到了全部的记录
在这里插入图片描述
或者输入名字不输入日期查询某一个人的记录
在这里插入图片描述
一个简单的识别系统就这样完成了!后续还可以通过设计UI界面和提供CS架构实现数据传输。

2017-09-19 23:53:00 rjgcszlc 阅读数 2612
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5905 人正在学习 去看看 杨波

项目用到的技术点和亮点

  1. 语音录音 (单个和列表)
  2. 语音播放(单个和列表)
  3. 语音录音封装
  4. 语音播放器封装
  5. 语音列表顺序播放
  6. 语音列表单个播放 复用问题处理

因为安装原生录音不能录mp3格式文件 而mp3格式是安卓和ios公用的,所以我们需要的是能直接录取mp3文件或者录完的格式转成mp3格式
下面添加这个库 能直接录mp3文件,我觉得是最方便的
compile ‘com.czt.mp3recorder:library:1.0.3’

1. 语音录音封装
代码简单 自己看吧

package com.video.zlc.audioplayer;

import com.czt.mp3recorder.MP3Recorder;
import com.video.zlc.audioplayer.utils.LogUtil;

import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
 * @author zlc
 */
public class AudioManage {

    private MP3Recorder mRecorder;
    private String mDir;             // 文件夹的名称
    private String mCurrentFilePath;
    private static AudioManage mInstance;

    private boolean isPrepared; // 标识MediaRecorder准备完毕
    private AudioManage(String dir) {
        mDir = dir;
        LogUtil.e("AudioManage=",mDir);
    }

    /**
     * 回调“准备完毕”
     * @author zlc
     */
    public interface AudioStateListenter {
        void wellPrepared();    // prepared完毕
    }

    public AudioStateListenter mListenter;

    public void setOnAudioStateListenter(AudioStateListenter audioStateListenter) {
        mListenter = audioStateListenter;
    }

    /**
     * 使用单例实现 AudioManage
     * @param dir
     * @return
     */
    public static AudioManage getInstance(String dir) {
        if (mInstance == null) {
            synchronized (AudioManage.class) {   // 同步
                if (mInstance == null) {
                    mInstance = new AudioManage(dir);
                }
            }
        }
        return mInstance;
    }

    /**
     * 准备录音
     */
    public void prepareAudio() {

        try {
            isPrepared = false;
            File dir = new File(mDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            String fileName = GenerateFileName(); // 文件名字
            File file = new File(dir, fileName);  // 路径+文件名字
            //MediaRecorder可以实现录音和录像。需要严格遵守API说明中的函数调用先后顺序.
            mRecorder = new MP3Recorder(file);
            mCurrentFilePath = file.getAbsolutePath();
//          mMediaRecorder = new MediaRecorder();
//          mCurrentFilePath = file.getAbsolutePath();
//          mMediaRecorder.setOutputFile(file.getAbsolutePath());    // 设置输出文件
//          mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);    // 设置MediaRecorder的音频源为麦克风
//          mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);    // 设置音频的格式
//          mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);    // 设置音频的编码为AMR_NB
//          mMediaRecorder.prepare();
//          mMediaRecorder.start();
            mRecorder.start();   //开始录音
            isPrepared = true; // 准备结束
            if (mListenter != null) {
                mListenter.wellPrepared();
            }
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.e("prepareAudio",e.getMessage());
        }

    }

    /**
     * 随机生成文件名称
     * @return
     */
    private String GenerateFileName() {
        // TODO Auto-generated method stub
        return UUID.randomUUID().toString() + ".mp3"; // 音频文件格式
    }


    /**
     * 获得音量等级——通过mMediaRecorder获得振幅,然后换算成声音Level
     * maxLevel最大为7;
     * @return
     */
    public int getVoiceLevel(int maxLevel) {
        if (isPrepared) {
            try {
                mRecorder.getMaxVolume();
                return maxLevel * mRecorder.getMaxVolume() / 32768 + 1;
            } catch (Exception e) {
                 e.printStackTrace();
            }
        }
        return 1;
    }

    /**
     * 释放资源
     */
    public void release() {
        if(mRecorder != null) {
            mRecorder.stop();
            mRecorder = null;
        }
    }

    /**
     * 停止录音
     */
    public void stop(){
        if(mRecorder!=null && mRecorder.isRecording()){
            mRecorder.stop();
        }
    }

    /**
     * 取消(释放资源+删除文件)
     */
    public void delete() {
        release();
        if (mCurrentFilePath != null) {
            File file = new File(mCurrentFilePath);
            file.delete();    //删除录音文件
            mCurrentFilePath = null;
        }
    }

    public String getCurrentFilePath() {
        return mCurrentFilePath;
    }

    public int getMaxVolume(){
        return mRecorder.getMaxVolume();
    }

    public int getVolume(){
        return mRecorder.getVolume();
    }
}

2. 语音播放器封装

package com.video.zlc.audioplayer.utils;

import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;

/**
 *
 * @author zlc
 *
 */
public class MediaManager {

    private static MediaPlayer mMediaPlayer;   //播放录音文件
    private static boolean isPause = false;

    static {
        if(mMediaPlayer==null){
            mMediaPlayer=new MediaPlayer();
            mMediaPlayer.setOnErrorListener( new MediaPlayer.OnErrorListener() {

                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {
                    mMediaPlayer.reset();
                    return false;
                }
            });
        }
    }


    /**
     * 播放音频
     * @param filePath
     * @param onCompletionListenter
     */
    public static void playSound(Context context,String filePath, MediaPlayer.OnCompletionListener onCompletionListenter){

        if(mMediaPlayer==null){
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setOnErrorListener( new MediaPlayer.OnErrorListener() {
                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {
                    mMediaPlayer.reset();
                    return false;
                }
            });
        }else{
            mMediaPlayer.reset();
        }
        try {
            //详见“MediaPlayer”调用过程图
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mMediaPlayer.setOnCompletionListener(onCompletionListenter);
            mMediaPlayer.setDataSource(filePath);
            mMediaPlayer.prepare();
            mMediaPlayer.start();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            LogUtil.e("语音error==",e.getMessage());
        }
    }


    /**
     *  暂停
     */
    public synchronized static void pause(){
        if(mMediaPlayer!=null && mMediaPlayer.isPlaying()){
            mMediaPlayer.pause();
            isPause=true;
        }
    }

    //停止
    public synchronized static void stop(){
        if(mMediaPlayer!=null && mMediaPlayer.isPlaying()){
            mMediaPlayer.stop();
            isPause=false;
        }
    }

    /**
     * resume继续
     */
    public synchronized static void resume(){
        if(mMediaPlayer!=null && isPause){
            mMediaPlayer.start();
            isPause=false;
        }
    }

    public static boolean isPause(){
        return isPause;
    }

    public static void setPause(boolean isPause) {
        MediaManager.isPause = isPause;
    }

    /**
     * release释放资源
     */
    public static void release(){
        if(mMediaPlayer!=null){
            isPause = false;
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    public synchronized static void reset(){
        if(mMediaPlayer!=null) {
            mMediaPlayer.reset();
            isPause = false;
        }
    }

    /**
     * 判断是否在播放视频
     * @return
     */
    public synchronized static boolean isPlaying(){

        return mMediaPlayer != null && mMediaPlayer.isPlaying();
    }
}

3. 语音列表顺序播放

 private int lastPos = -1;
    //播放语音
 private void playVoice(final int position, String from) {

        LogUtil.e("playVoice position",position+"");
        if(position >= records.size()) {
            LogUtil.e("playVoice","全部播放完了");
            stopAnimation();
            MediaManager.reset();
            return;
        }

        String voicePath = records.get(position).getPath();
        LogUtil.e("playVoice",voicePath);
        if(TextUtils.isEmpty(voicePath) || !voicePath.contains(".mp3")){
            Toast.makeText(this,"语音文件不合法",Toast.LENGTH_LONG).show();
            return;
        }

        if(lastPos != position && "itemClick".equals(from)){
            stopAnimation();
            MediaManager.reset();
        }
        lastPos = position;

//获取listview某一个条目的图片控件
        int pos = position - id_list_voice.getFirstVisiblePosition();
        View view = id_list_voice.getChildAt(pos);
        id_iv_voice = (ImageView) view.findViewById(R.id.id_iv_voice);
        LogUtil.e("playVoice position",pos+"");

        if(MediaManager.isPlaying()){
            MediaManager.pause();
            stopAnimation();
        }else if(MediaManager.isPause()){
            startAnimation();
            MediaManager.resume();
        }else{
            startAnimation();
            MediaManager.playSound(this,voicePath, new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mediaPlayer) {
                    //播放完停止动画 重置MediaManager
                    stopAnimation();
                    MediaManager.reset();

                    playVoice(position + 1, "loop");
                }
            });
        }
    }

4. 语音列表单个播放 复用问题处理
播放逻辑基本同上

  private int lastPosition = -1;
    private void playVoice(FendaListInfo.ObjsEntity obj, int position) {
        String videoPath = obj.path;
        if(TextUtils.isEmpty(videoPath) || !videoPath.contains(".mp3")){
            Toast.makeText(this,"语音文件不合法",Toast.LENGTH_LONG).show();
            return;
        }
        if(position != lastPosition){  //点击不同条目先停止动画 重置音频资源
            stopAnimation();
            MediaManager.reset();
        }
        if(mAdapter!=null)
            mAdapter.selectItem(position, lastPosition);
        lastPosition = position;

        id_iv_voice.setBackgroundResource(R.drawable.animation_voice);
        animationDrawable = (AnimationDrawable) id_iv_voice.getBackground();
        if(MediaManager.isPlaying()){
            stopAnimation();
            MediaManager.pause();
        }else if(MediaManager.isPause()){
            startAnimation();
            MediaManager.resume();
        }else{
            startAnimation();
            MediaManager.playSound(this,videoPath, new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    LogUtil.e("onCompletion","播放完成");
                    stopAnimation();
                    MediaManager.stop();
                }
            });
        }
    }

//核心方法
 //点击了某一个条目 这个条目isSelect=true 上一个条目isSelect需要改为false 防止滑动过程中 帧动画复用问题
    public void selectItem(int position, int lastPosition) {

        LogUtil.e("selectItem"," ;lastPosition="+lastPosition+" ;position="+position);
        if(lastPosition >= 0 && lastPosition < mDatas.size() && lastPosition != position){
            FendaListInfo.ObjsEntity bean = mDatas.get(lastPosition);
            bean.isSelect = false;
            mDatas.set(lastPosition, bean);
            notifyDataSetChanged();
        }

        if(position < mDatas.size() && position != lastPosition){
            FendaListInfo.ObjsEntity bean = mDatas.get(position);
            bean.isSelect = true;
            mDatas.set(position,bean);
        }
    }
/**
 * 适配器图片播放的动画处理
 */
private void setVoiceAnimation(ImageView iv_voice, FendaListInfo.ObjsEntity obj) {

        //处理动画复用问题
        AnimationDrawable animationDrawable;
        if(obj.isSelect){
            iv_voice.setBackgroundResource(R.drawable.animation_voice);
            animationDrawable = (AnimationDrawable) iv_voice.getBackground();
            if(MediaManager.isPlaying() && animationDrawable!=null){
                animationDrawable.start();
            }else{
                iv_voice.setBackgroundResource(R.drawable.voice_listen);
                animationDrawable.stop();
            }
        }else{
            iv_voice.setBackgroundResource(R.drawable.voice_listen);
        }
    }

5. 联系方式
qq:1509815887@qq.com
email : zlc921022@163.com
phone : 18684732678

6.下载地址

点击去下载

2017-03-29 10:13:01 xiangz_csdn 阅读数 35524
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5905 人正在学习 去看看 杨波

[转] http://www.leiphone.com/news/201703/JNPkCt08zJd9znzZ.html


自 2015 年 11 月首次发布以来,TensorFlow 凭借谷歌的强力支持,快速的更新和迭代,齐全的文档和教程,以及上手快且简单易用等诸多的优点,已经在图像识别、语音识别、自然语言处理、数据挖掘和预测等 AI 场景中得到了十分广泛的应用。

在所有这些 AI 应用场景中,或许是源于视觉对人类的直观性和重要性,图像识别成为其中发展速度最快的一个。目前,该技术已经逐渐趋于成熟,并在人脸和情绪识别、安防、医疗筛查和汽车壁障等诸多领域都取得了重大成功。

在这种情况下,对于绝大多数的 AI 开发者而言,利用 TensorFlow 自己亲手搭建一个图像识别模块,就成了一项最顺理成章的挑战。

为了帮助有志于此的开发者实现目标,也为了向那些亲手搭建过图像识别模块的技术达人提供一个与行业大牛交流、学习的机会,本次公开课雷锋网 AI 研习社有幸邀请到才云科技(Caicloud.io)联合创始人、首席大数据科学家郑泽宇,他将深入到代码层为我们详细讲解究竟怎么用 Tensorflow 自己亲手搭建一套图像识别模块。

  嘉宾介绍

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

郑泽宇,畅销书《TensorFlow:实战 Google 深度学习框架》作者,才云科技(Caicloud.io)联合创始人、首席大数据科学家。才云科技(Caicloud.io)是一家云计算人工智能创业公司。最近推出全球首个商用 TensorFlow as a Service (TaaS) 深度学习平台和行业大数据解决方案,用数据助力企业成长。

郑泽宇曾在 Google 担任高级工程师,作为主要技术人员参与并领导了多个大数据项目。提出并主导的产品聚类项目用于衔接谷歌购物和谷歌知识图谱(Knowledge Graph)数据,使得知识卡片形式的广告逐步取代传统的产品列表广告,开启了谷歌购物广告在搜索页面投递的新纪元。

郑泽宇拥有 CMU 计算机硕士学位,在机器学习、人工智能领域有多年研究经验, 并在 SIGIR、SIGKDD、ACL、ICDM、ICWSM 等顶级国际会议上发表多篇学术论文。

  公开课内容

本次公开课的主要内容包括:

1. 深度学习简介

2. 神经网络工作原理

3. 用 TensorFlow 实现图像识别

4. 疑难问题解答及讨论

以下为公开课完整视频:共 55 分钟。

 

以下为公开课内容的文字及 PPT 整理。

大家好,今天我想跟大家分享一些深度学习算法在图像识别上的应用。

主要内容大概可以分为如下三个部分:

● 深度学习介绍;

● 神经网络工作原理;

● 用 TensorFlow 实现图像识别。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

深度学习介绍

我们知道深度学习现在是已经是一个非常热门的技术,那大家第一次听到这个深度学习的时候呢?很有可能就是因为去年这个时候 AlphaGo 战胜李世石的这个事情。当时这个事情获得了全社会的广泛关注和热烈讨论。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

但其实 AlphaGo 并不是第一个战胜人类的智能机器人,早在 20 年前就有过 IBM 深蓝机器人战胜人类国际象棋冠军的记录。那为什么 AlphaGo 可以引发这么大的关注呢?

我认为原因有两个方面:一是围棋的复杂度远超国际象棋;二是因为深度学习技术的发展,它推动了不仅是一个 AlphaGo 这样的应用程序,它其实也推动了各个方面的人工智能的应用。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

例如谷歌的无人车项目,就是一个距离我们更近的例子。大家都知道,现在无论在国内还是国外,堵车和停车的问题其实都是很严重的。但是如果有了无人车这样一个工具,就能大大提升车主的用户体验。

正是因为深度学习技术在 AlphaGo 和无人车等项目背后的巨大推动力,才使得这些项目能够发展到目前这样一个比较高的水平。

但其实,深度学习技术并不是一个新技术,它的起源可以追溯到几十年前,只是这两年才得到了一个比较快速的发展。如图中所示的搜索热度变化图也能看出,从 2012 年到 2016 年,深度学习这个词的受关注程度是处于一个指数级增长的状态。那为什么会在 2012 年这个时间点出现这样的一个增长呢?其实是因为 ImageNet 这样一个图像识别的比赛。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

ImageNet 是由李飞飞教授发起的一个著名的图像识别数据集,里面包括了各种各样的图片,然后参赛者需要将物体从图片中标记出来,做一个分类。这个比赛每年都会举办,而且每次的比赛细节都会有所不同。然后在 2012 年的时候,由于深度学习的作用,比赛结果产生了一个巨大突破。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

从下图可以看出,在 2012 年之前,采用了传统分类算法的时候,错误率大概是 26% 左右。而且我们可以看到这个错误率的变化趋势非常缓慢,也就是说要减低一点错误率,就必须付出巨大的努力。但是在引入深度学习算法之后,从 2011 到 2012 年,使得错误率从 26% 一下子降低到了 16%,并且从 2012 年开始的这三四年中,错误率还在以每年 4% 左右的一个速度在降低。到 2015 年,机器的识别率已经接近了人类的水平,即 5.1% 的错误率。2016 年,机器最新的识别正确率已经达到了 97% ,错误率达到了 3% 左右,已经比人类好得多。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

如下图是才云科技集成 Google 训练好的一个模型,它可以通过分析用户上传的图片,例如一头牛,然后分析出这头牛是某个品种的概率是多少。还有现在大家在 Google 或者百度上传一张图片,然后搜索引擎就能比较精确地告诉你这个图片里的主体是什么动物,或者什么植物,甚至还能提供一段简单的文字描述等等。其实这些都是基于深度学习的算法来实现的。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

除了图像识别之外,其实深度学习的早期应用是数字识别。例如图中所示的这个就是 1998 年的时候 Yann LeCun 教授做的一个手写体数字识别的项目(如下图所示),用的就是深度学习技术。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

此外还有 Google 在地图上的应用,能够通过图像识别自动定位门牌号和地理位置等信息。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

其实神经网络,也就是深度学习技术用在图像识别的应用还有很多,包括人脸识别、安防领域、火警、群众事件,还有现在的美颜等等。并且,受到神经网络思想的启发,目前包括语音识别、机器翻译、自然语言处理和大数据分析、预测等行业都得到一个比较大的发展。

神经网络原理

下面简单介绍一下神经网络的情况。

这里我不会去推导前向传播或者反向传播的具体过程。前向传播的过程比较简单,这里会做一个大致的介绍。而反向传播对数学的要求比较高,并且通过 TensorFlow 可以非常容易地去实现,因此我只会简单介绍一下基本原理,大家自己有兴趣的话可以自己去深挖。这个部分我主要还是做一些流程上的介绍,让大家知道一个完整的机器学习或者说图像识别问题,我该怎样一步一步地去解决它,包括我需要做哪些事,什么事情可以通过什么工具来帮助我实现。

首先我们来介绍一下什么是深度学习。大家可能经常会听到深度学习和神经网络这些词,但它们之间究竟是一个什么关系呢?其实,深度学习在维基百科上的定义是:多层的非线性变换的一个算法合集。那怎样实现这个多层非线性变换呢?最好最方便的一种方法就是神经网络。所以说基本上当前来讲的深度学习,就等于是深层神经网络的一个代名词。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

神经网络,大家从这个词就能看出它多多少少跟人脑的神经网络有些关联。如图所示,左上角就是一个神经元的模型,他包括很多不同的输入,以及一个轴壳来处理这些输入,最终得到一个输出。这个对应到人工神经网络也是一样的,它会有多个输入,然后经过一些变换得到输出。但人脑的变换函数具体是怎样的,现在还弄不清楚,我们只能通过这样一组加权和,加上一个激活函数来模拟人脑。

大家都知道,人脑是通过这种神经网络的网络状结构处理信息的,因此在人工神经网络结构中也是通过这样的一种多层结构来实现神经元的连接。如上图的右下角所示,每一个圆圈都代表一个神经元,多个输入,一个输出,上一层的输出作为下一层的输入。

下面我简单介绍一下怎么用这个网络结构来处理问题。比如数字识别问题,如图所示的数字图像在计算机里就是一个像素矩阵,然后每个矩阵元素里面都是各种各样的一个数字,我们把这些数字作为这个神经网络的输入层提供进来,然后通过不同结构的神经网络处理,从输入层到隐藏层再到输出层,就可以得到处理结果。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

比如图中的这个手写数字问题,其实就是要识别这个数字到底和 0 到 9 中的哪个数字更像一点。我们可以安排输出层有 10 个结点,分别代表 0 到 9 的 10 个数字。那么如果这个图像是 1 的话,我们就希望代表 1 的这个结点输出的结果是 1 ,其他的结点是 0 。这样的话假设我们输入一张图像,然后发现代表数字 7 的结点输出为 1,其他结点为 0 ,那么我们就等于识别出了这个图像的内容是 7,也就完成了一个数字识别的任务。

这只是一个最简单的例子,还有更多更复杂的神经网络这里我们就不做具体介绍了,感兴趣的朋友可以关注才云科技开源的一个代码库,里面有各种丰富的样例代码。(雷锋网(公众号:雷锋网)注:开源地址见下文链接)

下面简单介绍一下深度学习的两个大的分类:监督学习和非监督学习。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

我通常会把机器学习和人类学习来做一个对比,其实有很多地方都是比较类似的。例如我们小时候去上课,然后考试。其实机器学习也是一个类似的过程,它通过海量的数据,然后学习(总结)出一些对应的规律,并将规律应用在一个测试环境上,就能得到一个诸如正确率这样的评测指标,最终通过评测指标来评测一个机器学习算法的好坏。

那么在监督学习的场景中,通过海量的标记过的数据,就相当于人类在学习中通过大量的做题,然后每做一个题都知道对错,没有错的话就加强这个过程,有错的话就反向改进,这就是监督学习。

而非监督式就不需要人为的标签数据,它是监督式和强化学习等策略之外的一个选择。典型的非监督学习有聚类等,它是直接从数据中寻找相似性,即规律。

现在其实机器学习除了监督式和非监督式之外,还有增强学习。它是和监督式及非监督式都有些不同的一种方式。然后,但其实在工业界用得最多的,其实还是监督式学习的方式,非监督式和增强学习现在在工业界的使用还是相对比较少。那其实我们今天也会主要以监督式的这种学习方式作为一个样例,来告诉大家怎样去完成一个监督式的学习方法来完成图像识别工作。

在具体介绍整个模型之前,这里我先跟大家详细介绍一下神经网络的工作原理。

刚刚讲到,我们要去做一个图像分类模块,就相当于是把这个图像的原始的像素矩阵传入输入层,然后经过一层一层的推导,得到输出层。那其实神经网络里面一个最关键的部分,就是说我怎样通过一层一层的网络结构来得到从输入层到输出层的结果。最简单的来讲,我们先要知道一个单一的神经元的工作方式。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

如图所示,我们其实可以将每个神经元视为这样的一个多项式组合,w表示权重,b表示偏移量。那么训练时一个最重要的工作就是找到最合适的权重,使得我们的实际输出与预想输出一致。那么输入的一个加权平均,加一个偏移,最后再通过一个激活函数,最后就能得到输出。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

这里我们以一个判断零件是否合格的例子来讲解,注意这里省略了偏移量和激活函数。比如说我这里的每一个零件它都会有长度、质量等参数,就相当于收集了很多关于这个零件的数据,其中有一些我们知道是已经合格了的,也就相当于有了一个训练数据。将这些训练数据输入模型,通过结果对比不断调节隐藏层的权重参数,最终达到一定的正确率之后,也就是完成了模型训练。接着我们可以测量任意一个零件的参数,把测量数据输入神经网络,就能判断这个零件是否合格。

这样的一个过程其实就是正向传播的一个过程。反向传播相当于是我知道一个零件的长度和质量,也知道它是否合格的时候,再去根据这个目标做一个反向的回馈。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

这里我列出了一些简单的激活函数,注意这里所有的激活函数都是非线性的函数。如果一个模型没有激活函数的话,就相当于是所有线性过程的叠加,不论模型有多少层,叠加出来还是一个线性过程,也就是模型没有涉及非线性的因素,也就不会有实际的应用。而激活函数就是一个提供非线性过程的因子,有了激活函数我们才能完成一个非线性变化的过程。

更详细的内容大家可以参考《TensorFlow:实战Google深度学习框架》这本书。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

这里我们介绍两种常用的激活函数:Softmax 和 Sigmoid。在多分类的问题中,经过层层的推导,其实每个节点的输出都是不一定的。那么如果我们想得到一个概率分布,就需要用到 Softmax 这样一个层。它相当于对每个输出节点的大小作为置信度做一个归一化的操作,然后使得每一个最终输出节点的值都在 0 到 1 之间,这样就等于输出了一个概率分布。我们可以大概理解为不同的输出类别的一个概率分布。在多分类问题中,一般都会用 Softmax 作为最后的处理层,得到一个概率分布情况。类似的,在二分类中我们通常使用 Sigmoid 函数。

这样,从输入层到隐藏层到输出层再到激活函数,我们等于介绍了全连接神经网络的一个基本结构。刚刚提到,监督学习就是我们得到一个结果之后,能够判断它的好坏。那么怎么判断和评测呢,就需要损失函数。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

通过损失函数,我们可以计算出实际输出和真实标签的差别,然后进行优化,缩小这种差别。两个经典的损失函数一个是交叉熵,另一个是 MSE 最小平方差。他们详细的用法,包括自定义函数怎么样去定义等内容,大家可以参考《TensorFlow:实战Google深度学习框架》这本书。

定义完损失函数,我们再来讲讲优化。

首先,优化的是什么?我们刚刚提到,神经网络里的每一个神经元都有参数,我们要优化就是这些参数。那怎样来优化呢?就是通过我们定义的损失函数,即推导得出的结果要跟真实结果越接近越好。这相当于变成了一个最小化问题。最小化问题确实有比较成熟的数学解法。但对于神经网络这么复杂的一个结构,它其实并没有特别好的数学公式能够直接求解。

虽然说,优化神经网络的算法有不少,但主体的思想其实都是基于梯度下降,或者随机梯度下降。那么,梯度下降是什么?用简单的话来讲,它就相当于是把不同参数的取值和损失函数的大小,看成空间中的一个曲面。最简单的情况下,可看成二维空间上的一条曲线。任意一个参数的取值,就对应了损失函数的取值。这样子的话,我们就可以通过计算它的梯度来修改参数,使得损失函数会往更小的这样一个方向去发展。这就是优化神经网络的最基本的思想。包括反向传播算法,其实也就是怎么更快地去计算出每一个参数的梯度,使得计算的时间复杂度减少。优化神经网络的核心思想其实还是梯度下降。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

梯度下降是在所有的数据集上去算它的梯度。但神经网络的数据量一般会比较大,一般很难把所有的数据全都用上。这样的时候,我们一般会采用 minibatch。一次计算部分的梯度来减少计算量。本来我们就知道,梯度下降其实就已经无法达到全局最优值。那使用随机梯度下降呢,其实就更加难以保证达到最小值,有全部训练数据集才能有最小值。但我们可以通过调整学习率,使它更加逼近极小值。

这些就是我想给大家讲的,神经网络的大致工作原理。其实这个工作原理讲得比较粗浅,也比较 high-level。但大部分人其实对主要的一些部分了解就 OK 了。

用 TensorFlow 实现图像识别

接下来,我会把这些原理对应到具体的代码里面,然后让大家知道怎么样通过 TensorFlow 来实现图像识别。同时讲解怎么用 TensorFlow 来实现大致的机器学习分类算法。

虽然上面有很多的细节没有覆盖,但其实 TensorFlow 也把这样的一些细节给屏蔽掉了。我的意思是,其实大家只需要理解一些比较 high-level 的东西,就完全能够通过 TensorFlow 来实现神经网络的训练。

我们先谈什么是 TensorFlow,为什么要选择 TensorFlow。

TensorFlow 是谷歌在 2015 年底开源的一个深度学习框架。虽然说是深度学习,官方来讲,其实 Google 希望把它做成一个计算工具。但这个计算工具的主要任务,就是用来实现深度学习算法。所以说,其他的功能我们暂时也就抛开不谈。就深度学习的基本功能来讲,TensorFlow 已经得到广泛的应用。Google自不必提,现在所有 Google 内部的深度学习系统全都是基于TensorFlow。DeepMind 卖了之后,推出的东西也都会基于TensorFlow。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

Google 之外,国内包括华为、京东、小米,其实都在用 TensorFlow。国外像 Twitter、Uber、Snapchat 也在用。使用它的公司其实非常多。现在学术界也好,工业界也好,都偏向使用它。为什么 TensorFlow 会这么受欢迎?除了有大公司的背书,社区的贡献度也是非常重要的一个参考指标。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

在 GitHub,无论是从 star 的数量,fork 的数量,还是从 issues 和 pull request, TensorFlow 都遥遥领先于其他同类深度学习开源框架。对于深度学习来讲,它还是一门正在发展中的技术。对于工具来讲,我们认为,它是否能够跟上这门技术的发展,其实是非常重要的一个考核标准。就是说,因为技术的发展是非常快的,如果工具的发展落后于技术的发展,它就会有一个被淘汰的风险。所以说,当它的社区活跃度非常高的时候,这个风险度就会相应的降低。因此我们比较推荐使用 TensorFlow。对于新手来讲,我们也比较推荐。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

这里给出了一个简单的 TensorFlow 的 Hello World 的程序,实现了一个简单的向量加法的操作。首先 import 了 TensorFlow ,然后定义了 a、b 两个向量,包括 name 属性,接着把它们加起来。这里 TensorFlow 采用了这样的一个惰性计算模型,输出的并不是加法的结果,而是结果的一个引用。另外,要运行整个 result 运算,就必须定义一个 session ,session 会掌握所有 TensorFlow 的运算资源,然后通过 session 运行相关操作。

这里只是简单介绍了一个 TensorFlow 的简单用法,由于时间有限,也无法深入地去详细介绍。我们关注的是如何用 TensorFlow 实现一个神经网络的全连接,也就是加权和,加上激活函数的模型。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

加权和可以通过矩阵乘法的方式实现。如上图代码所示,这里通过 placeholder 的方法定义了一个 input ,其中类型是必须的,其他的诸如 shape 等参数则可以等使用的时候再赋值。之后定义了权重 w 和偏移量 b,这里是通过 Variable 方法定义的,这样等最后优化的时候,TensorFlow 会针对这些 Variable 展开优化。最后通过乘法和加法操作,也就是通过 output = tf.nn.relu(tf.matmul(x, w) + b) 这一行代码就实现了神经网络的基本结构。

这里是通过基础的 TensorFlow API 实现的,如果通过 Keras 等更高层的封装来实现会更加简单。这里我们是从基础的 API 入手来进行讲解,如果大家对高层封装感兴趣,可以自己学习。需要指出的是,其实高层封装和基础 API 的主要区别是实现上的区别,他们整体上的过程是基本一样的。

下面我们来看一下如何用 TensorFlow 实现一个具体的图像识别模块,即从 MNIST 数据集中识别手写数字。(完整代码见下文链接)

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

可以看到,TensorFlow 通过 read_data_sets 方法对引用数据进行了一个非常好的封装,后续可以通过非常简单的方式读取这些划分好的数据集,例如通过 train、validation、test 等关键词就可以读取训练和测试数据集等。

如下图所示,然后是通过 next_batch 来获取一小批的训练数据。我们刚刚提到,在利用梯度下降算法时需要在所有的训练数据上计算梯度,但是计算量太大了,因此这里通过 next_batch 方法,相当于我们在所有的训练数据集中筛选一部分,随机选取一部分训练数据集,提供到神经网络的输入层,然后通过反向迭代方法去优化这个神经网络。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

这里 xx 设置等于 100,也就是我们得到了 xs 和 ys 两个矩阵,xs 代表输入数组,相当于把一个 28×28 的手写图像展开成一个长度为 748 的一维数组。ys 相当于我们的结果,也就是 0-9 这 10 种可能值。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

如上图,完了之后是一个前向传播的一个大致过程的程序截图。这个过程就相当于是定义一层一层的神经网络结构,这里 inference 函数中的 input_tensor 相当于输入矩阵,后面的 reqularizer 就相当于一个正则化的东西。我们可以看到,当输入来了之后,程序开始进行一层一层的推导,定义一层一层的权重和偏移量,算出每一层的结果,传入下一层,进入下一层的计算。

其实通过这个前项传播的定义我们可以看到,无论是全连接层还是卷积神经网络,甚至是循环神经网络,它的大致流程都是一样的,给定输入,然后通过一层一层的传递就可以得到最后的输出。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

如上图,下面我们看一下主程序。其实主程序就是调用了 train 的过程,这个 train 的过程其实也是比较简单的。第一部分是定义输入,也就是怎样来提供这个训练数据的接口,也就是通过 placeholder 的方式导入,输入这里是 x ,输出是 y_ 。然后通过调用 inference 函数来进行一个前向传播的计算。然后定义了滑动平均和损失函数。

这里的过程其实是就相当于是:我通过输入一个训练数据集,然后得到在当前数据集下的推导结果,然后再通过这个推导结果和正确答案对比,就知道跟正确答案的差别在哪。下一步可以看到我们定义了 loss ,它相当于评估当前模型好坏的一个指标。这里其实就相当于是评价 cross_entropy 加上正则化,这里正则化是为了避免过耦合的。

完了之后,下一行是通过 GradientDescentOptimizer 函数优化。TensorFlow 提供了大概 5-7 中不同的优化函数可供选择,它们针对不同的应用场景,各具特点,大家可以灵活选择。这里我认为,对于那些不搞学术研究的同学,其实没有必要去从数学的角度推导每一个优化函数具体是怎么优化的,从应用层的角度来看,大部分用户只需要提供学习率和目标函数,并且了解这些优化函数的优劣就可以了,这个相对来说还是比较方便。

在把所有的这些计算方式都定义好了之后,下面就是生成 TensorFlow 的计算图,以及生成 session。定义好 session 之后,下面训练的过程就比较简单了,其实就是写了一个循环,每次选取一小部分训练数据,然后去做训练,隔一段时间再打印一下训练结果,整个过程就完成了。

所以说整个用 Tensorflow 来实现一个神经网络的过程,相对还是比较简单的。需要注意的是,这里我介绍的只是原生态的 TensorFlow,如果大家要去使用 TFLearn,或者 Keras 这些高级封装去实现 MNIST 问题的话,可能会更加简单,大概只需要 2-3 行代码就可以解决了。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

这里我们基本上把通过原生态的 Tensorflow 生成神经网络的过程为大家介绍了一下。其实后面还有个 evaluate 评估的部分(代码如上图所示),因为时间关系我就不对着代码详细讲了,感兴趣的同学可以自己下去研究(源码见下文链接)。

下面我再跟大家再介绍一下循环卷积神经网络。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

刚刚大家看到的这个结构是一个全链接的神经网络,在图像处理的过程中,使用全连接神经网络最大的一个问题就是它的参数太多了,这个问题可能会导致模型最终训练不好。

例如,经常发生的,当你的训练数据不足的时候,参数又太多,你就可能训练不出来。一个非常简单的例子,大家可以想象 N 元的一个方程组,然后我们假设只有 N 个数据,并且这些数据是完全可分的,也就是我们是可以完全求解。但完全求解可能会导致过拟合,因为训练数据在真实环境下都是有噪音的,也就是没有办法做到完全避免随机因素的影响。在这种情况下如果你过于贴合训练数据,那么就有可能没有办法去收敛到未知的数据。

所以这就是参数过多可能引发的问题,即过拟合和训练不出来。那怎样去解决这两个问题呢?卷积神经网络就是一个很好的方法。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

卷积神经网络就相当于是采用一个内核,即一个规模较小的矩阵,去处理一个比较小的区域,然后去通过移动这个小矩阵,去处理不同的这个块。这种方式一个直观的一个意义就是:一般的图像上相邻区域的内容是相似的。然后通过这样的一个潜在的东西,就可以去把一个浅层的表达变成一个更深层的表达。也就是相当于自动从图像中去提取特征。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

例如上图所示,第一层可能可以从图像中提取一些线条和斑点,第二层提取一些更复杂的性状。第三层和第四层,层数越多,提取的特征就会越复杂。然后通过最后提取出来的这样一些特征,我们再去做一个全连接的分类,最后这个分类的效果也就会更好。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

然后这里给出了一个简单的 LeNet5 的模型,我们可以看到他的大致结构就是从输入层,经过不断地卷积池化,再经过 1 到 3 层不等的全连接层,最后得到输出层。其实现在很多的卷积神经网络基本上也都保留了这样的一种结构。

除了这种模型之外,另一种比较特殊的模型是 Google Inception 模型,这里因为时间关系我也不去做过多的介绍了。

然后我在这里给出了一个简单的用 TensorFlow 的程序来实现卷积层。通过代码其实大家也可以看到,在这个框架里面,无论是全连接的神经网络也好,还是卷积神经网络也好,甚至循环神经网络也好。它们的训练过程,以及前面的准备过程其实基本上都是差不多的,你基本上只要去修改,怎么样去从输入得到输出就可以了。

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

从代码也可以看到,开始是定义这个卷积层中的权重和偏移量,完了之后 TensorFlow 会对这个卷积层有一个封装,然后通过 conv2d 函数得到一个 2D 的卷积层,然后再把偏移量、激活函数加上去。大家可以看到整个的过程还是比较简单的,同样的,如果用 Keras 等更高级的封装来实现的化会更加简单。

最后我要推荐一下《TensorFlow:实战Google深度学习框架》这本书,今天讲的内容,包括一些略去的内容,基本上全部在这本书里都有非常详细的介绍。

另外,前面提到的代码地址如下:

https://github.com/caicloud/tensorflow-tutorial 

代码库里包含了书中所有的样例代码,此外还包括了才云科技提供的 TensorFlow as a Service (TaaS) 深度学习平台的一些教程和代码,包括后面我们做的一些图像分类和图像识别的样例代码,后面也都会陆陆续续添加进去。大家有兴趣的化可以关注一下这个代码库。

今天的分享就到这里,谢谢大家!

  群友问题解答

问题1:麻烦老师推荐下 Coursera 的机器学习公开课?

答:吴恩达的“机器学习 Machine Learning”非常值得推荐的,那门课我个人至少看了两到三遍,而且在不同的阶段看都会有不同的感受。

网址:https://www.coursera.org/learn/machine-learning 

编者按:更多机器学习的精品公开课汇总,详见雷锋网相关文章“开发者入门必读:最值得看的十大机器学习公开课”。

文章链接:http://www.leiphone.com/news/201701/0milWCyQO4ZbBvuW.html 

问题2:请问 TensorFlow 相比较 Keras 有什么优势?

答:Keras 是对于 TensorFlow 一个更高层的封装,它几乎可以实现 TensorFlow 的所有功能,TensorFlow 则是更底层更加灵活的一个工具,相对来说 TensorFlow 的功能可能更全。但 Keras 一个最大的问题在于,目前不支持分布式,我们希望 Keras 在未来的版本更新中能够加入相关的特性支持。学习的话,可以先从 Keras 入手,因为它和 TensorFlow 没有本质的区别,它只是 TensorFlow 的一个高层封装,写起代码来会更加方便。

问题3:AI 学习最主要的是算法和建模对不对呀?

答:算法和模型只是一个方面,但其实我还是比较推荐做一些实战的应用,关键还是看你要做什么事情。如果你是要做学术研究,那就要多看论文,如果你是要做工程或者要找工作的话,建议你多做一些 Kaggle 的比赛。

问题4:TensorFlow 在推荐系统上的运用如何?

答:TensorFlow 官方有一个 Wide & Deep 的教程,是关于谷歌 App 推荐的一些内容,可以关注一下。TensorFlow 是一个兼容的框架,可以被应用在许多问题上,其实更多的是关于深度学习技术的应用,现在学术界有许多相关的研究,大家可以关注一下。

Wide & Deep 链接:https://www.tensorflow.org/tutorials/wide_and_deep 

问题5:请问 TensorFlow 相比较 Caffe 有什么不同?

答:TensorFlow 是一个相对更全面的工具,但是在图像处理上它的表现不如 Caffe 成熟,处理速度也会稍微慢一点。TensorFlow 相比 Caffe 的优点是支持分布式,而且发展的速度也比 Caffe 更快。

问题6:TensorFlow 实质是通过大量训练数据(监督学习)和神经网络算法,实现深度学习过程。未来能不能不需要大量训练数据,即非监督学习的办法就可以实现深度学习的过程或工具,Google 等巨头有这方面的尝试或计划吗?

答:非监督式学习包括增强学习其实也有尝试,它们其实也都可以通过 TensorFlow 来实现,具体怎么做大家可以参考官网的教程。TensorFlow 主要还是提供了一个计算框架,可以实现和神经网络相关的几乎所有算法。

问题7:TensorFlow 既然支持分布式,那 TensorFlow On Spark 的意义还大吗?

答:TensorFlow 支持的分布式其实只是一个计算框架,TensorFlow On Spark 的意义还是有的,但我觉得没有 TensorFlow On Kubernetes 的意义大,Spark 主要是做了一个调度和管理的系统,但是性能损失比较大,目前成熟的应用也比较少。大家如果想要了解更多分布式 TensorFlow 的内容,可以关注才云科技的平台。

问题8:我目前在用 Caffe,想着要不要转 TensorFlow,老师有什么建议么?

答:我觉得转 TensorFlow 还是有意义的,主要还是你具体用 TensorFlow 来做什么。可能因为我接触 TensorFlow 比较多,但是我个人认为 Caffe 其实正处在一个慢慢的被淘汰的边缘。而且目前客观上讲 TensorFlow 的确是一个无论在工业上还是学术上都非常流行的框架。


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