人脸年龄识别属于人脸属性识别的范畴,人脸属性识别可对图片中的人脸进行检测定位,并识别出人脸的相关属性(如年龄、性别、表情、种族、颜值等)内容。不同属性识别的算法可以相同,也可以不同。rude-carnie是做年龄识别和性别识别的一个开源项目,基于TensorFlow,源代码网址:http://www.github.com/dpressel/rude-carnie。下面我们基于这个项目源码来讲年龄识别。
我们可以把年龄划分为几个段[’(0, 2)’,’(4, 6)’,’(8, 12)’,’(15, 20)’,’(25, 32)’,’(38, 43)’,’(48, 53)’,’(60, 100)’],然后基于分类的思想来做年龄预测问题。用下面的脚本命令:
python3 guess.py --model_type inception --model_dir /home/hadoop/chongdianleme/ nianling/22801/inception/22801 --filename /home/hadoop/chongdianleme/data/myimages/baidu1.jpg
基于训练好的年龄模型和人脸图片就能预测出年龄。但是有一个问题,直接这样预测不是很准,因为图片没有经过任何处理。我们可以通过opencv和上面讲到的FaceNet的人脸检测和对齐算法来做。但opencv比较简单,FaceNet的人脸检测和对齐效果比较好,我们可以使用前面提供的http服务接口http://172.17.100.216:8816/detectAndAlignedService来处理,然后把检测和对齐后的人脸图片传给guess.py,这样预测处理的效果精准很多。
另外一个问题是,因为训练的模型用的是开源项目训练好的,是拿外国人的人脸数据训练,这样用来预测我们中国人的年龄会有一些差异,最好的方式是拿我们中国人自己的人脸年龄数据做训练,这样预测才会更好。
针对年龄识别和性别识别都是用guess.py这个文件,年龄识别是多分类,性别是二分类。我们看下guess.py的源码,如代码8.5所示:
【代码8.5】 guess.py
代码如下(示例):
from __future__ import absolute_importfrom __future__ import divisionfrom __future__ import print_functionfrom datetime import datetimeimport mathimport timefrom data import inputsimport numpy as npimport tensorflow as tffrom model import select_model,get_checkpointfrom utils import*import osimport jsonimport csvRESIZE_FINAL=227#性别有两个GENDER_LIST=['M','F']#年龄是分段的,可以看成多分类任务AGE_LIST=['(0, 2)','(4, 6)','(8, 12)','(15, 20)','(25, 32)','(38, 43)','(48, 53)','(60, 100)']MAX_BATCH_SZ=128#模型文件目录tf.app.flags.DEFINE_string('model_dir','','Model directory (where training data lives)')#性别和年龄都是用的这个,通过参数age|gender来区分tf.app.flags.DEFINE_string('class_type','age','Classification type (age|gender)')#用cpu还是用GPU来训练tf.app.flags.DEFINE_string('device_id','/cpu:0','What processing unit to execute inference on')tf.app.flags.DEFINE_string('filename','','File (Image) or File list (Text/No header TSV) to process')tf.app.flags.DEFINE_string('target','','CSV file containing the filename processed along with best guess and score')#检查点tf.app.flags.DEFINE_string('checkpoint','checkpoint','Checkpoint basename')tf.app.flags.DEFINE_string('model_type','default','Type of convnet')tf.app.flags.DEFINE_string('requested_step','','Within the model directory, a requested step to restore e.g., 9000')tf.app.flags.DEFINE_boolean('single_look',False,'single look at the image or multiple crops')tf.app.flags.DEFINE_string('face_detection_model','','Do frontal face detection with model specified')tf.app.flags.DEFINE_string('face_detection_type','cascade','Face detection model type (yolo_tiny|cascade)')FLAGS=tf.app.flags.FLAGSdefone_of(fname,types):returnany([fname.endswith('.'+ty)forty in types])defresolve_file(fname):ifos.path.exists(fname):returnfnameforsuffix in('.jpg','.png','.JPG','.PNG','.jpeg'):cand=fname+suffixifos.path.exists(cand):returncandreturnNonedefclassify_many_single_crop(sess,label_list,softmax_output,coder,images,image_files,writer):try:num_batches=math.ceil(len(image_files)/MAX_BATCH_SZ)pg=ProgressBar(num_batches)forj inrange(num_batches):start_offset=j*MAX_BATCH_SZend_offset=min((j+1)*MAX_BATCH_SZ,len(image_files))batch_image_files=image_files[start_offset:end_offset]print(start_offset,end_offset,len(batch_image_files))image_batch=make_multi_image_batch(batch_image_files,coder)batch_results=sess.run(softmax_output,feed_dict={images:image_batch.eval()})batch_sz=batch_results.shape[0]fori inrange(batch_sz):output_i=batch_results[i]best_i=np.argmax(output_i)best_choice=(label_list[best_i],output_i[best_i])print('Guess @ 1 %s, prob = %.2f'%best_choice)ifwriter is not None:f=batch_image_files[i]writer.writerow((f,best_choice[0],'%.2f'%best_choice[1]))pg.update()pg.done()except Exception as e:print(e)print('Failed to run all images')defclassify_one_multi_crop(sess,label_list,softmax_output,coder,images,image_file,writer):try:print('Running file %s'%image_file)image_batch=make_multi_crop_batch(image_file,coder)batch_results=sess.run(softmax_output,feed_dict={images:image_batch.eval()})output=batch_results[0]batch_sz=batch_results.shape[0]fori inrange(1,batch_sz):output=output+batch_results[i]output/=batch_szbest=np.argmax(output)best_choice=(label_list[best],output[best])print('Guess @ 1 %s, prob = %.2f'%best_choice)nlabels=len(label_list)ifnlabels>2:output[best]=0second_best=np.argmax(output)print('Guess @ 2 %s, prob = %.2f'%(label_list[second_best],output[second_best]))ifwriter is not None:writer.writerow((image_file,best_choice[0],'%.2f'%best_choice[1]))except Exception as e:print(e)print('Failed to run image %s '%image_file)deflist_images(srcfile):withopen(srcfile,'r')as csvfile:delim=','ifsrcfile.endswith('.csv')else'\t'reader=csv.reader(csvfile,delimiter=delim)ifsrcfile.endswith('.csv')or srcfile.endswith('.tsv'):print('skipping header')_=next(reader)return[row[0]forrow in reader]defmain(argv=None):# pylint:disable=unused-argumentfiles=[]print("target %s"%FLAGS.target)ifFLAGS.face_detection_model:print('Using face detector (%s) %s'%(FLAGS.face_detection_type,FLAGS.face_detection_model))face_detect=face_detection_model(FLAGS.face_detection_type,FLAGS.face_detection_model)face_files,rectangles=face_detect.run(FLAGS.filename)print(face_files)files+=face_filesconfig=tf.ConfigProto(allow_soft_placement=True)with tf.Session(config=config)as sess:label_list=AGE_LISTifFLAGS.class_type=='age'elseGENDER_LISTnlabels=len(label_list)print('Executing on %s'%FLAGS.device_id)model_fn=select_model(FLAGS.model_type)with tf.device(FLAGS.device_id):images=tf.placeholder(tf.float32,[None,RESIZE_FINAL,RESIZE_FINAL,3])logits=model_fn(nlabels,images,1,False)init=tf.global_variables_initializer()requested_step=FLAGS.requested_stepifFLAGS.requested_stepelseNonecheckpoint_path='%s'%(FLAGS.model_dir)model_checkpoint_path,global_step=get_checkpoint(checkpoint_path,requested_step,FLAGS.checkpoint)saver=tf.train.Saver()saver.restore(sess,model_checkpoint_path)softmax_output=tf.nn.softmax(logits)coder=ImageCoder()# Support a batch mode if no face detection modeliflen(files)==0:if(os.path.isdir(FLAGS.filename)):forrelpath in os.listdir(FLAGS.filename):abspath=os.path.join(FLAGS.filename,relpath)ifos.path.isfile(abspath)andany([abspath.endswith('.'+ty)forty in('jpg','png','JPG','PNG','jpeg')]):print(abspath)files.append(abspath)else:files.append(FLAGS.filename)# If it happens to be a list file, read the list and clobber the filesifany([FLAGS.filename.endswith('.'+ty)forty in('csv','tsv','txt')]):files=list_images(FLAGS.filename)writer=Noneoutput=NoneifFLAGS.target:print('Creating output file %s'%FLAGS.target)output=open(FLAGS.target,'w')writer=csv.writer(output)writer.writerow(('file','label','score'))image_files=list(filter(lambda x:x is not None,[resolve_file(f)forf in files]))print(image_files)ifFLAGS.single_look:classify_many_single_crop(sess,label_list,softmax_output,coder,images,image_files,writer)else:forimage_file in image_files:classify_one_multi_crop(sess,label_list,softmax_output,coder,images,image_file,writer)ifoutput is not None:output.close()if__name__=='__main__':tf.app.run()
年龄识别我们对外提供一个Web接口,guessAgeWeb_chongdianleme.py如代码8.6所示:
【代码8.6】 guessAgeWeb_chongdianleme.py
代码如下(示例):
from __future__ import absolute_importfrom __future__ import divisionfrom __future__ import print_functionfrom datetime import datetimeimport mathimport timefrom data import inputsimport numpy as npimport tensorflow as tffrom model import select_model,get_checkpointfrom utils import*import osimport csv#pip3 install flaskfrom flask import Flaskfrom flask import requestimport urllib# pip3 install requestsimport requests,urllib.requestfrom scipy import miscimport argparseimport facenetimport align.detect_faceimport jsonRESIZE_FINAL=227#性别有两个GENDER_LIST=['M','F']#年龄是分段的,可以看成多分类任务AGE_LIST=['(0, 2)','(4, 6)','(8, 12)','(15, 20)','(25, 32)','(38, 43)','(48, 53)','(60, 100)']MAX_BATCH_SZ=128defone_of(fname,types):returnany([fname.endswith('.'+ty)forty in types])defresolve_file(fname):ifos.path.exists(fname):returnfnameforsuffix in('.jpg','.png','.JPG','.PNG','.jpeg'):cand=fname+suffixifos.path.exists(cand):returncandreturnNonedeflist_images(srcfile):withopen(srcfile,'r')as csvfile:delim=','ifsrcfile.endswith('.csv')else'\t'reader=csv.reader(csvfile,delimiter=delim)ifsrcfile.endswith('.csv')or srcfile.endswith('.tsv'):print('skipping header')_=next(reader)return[row[0]forrow in reader]# 初始化class_type='age'device_id="/cpu:0"model_type="inception"requested_step=""#模型文件目录model_dir="/home/hadoop/chongdianleme/nianling/22801/inception/22801"checkpoint="checkpoint"config=tf.ConfigProto(allow_soft_placement=True)sess=tf.Session(config=config)with sess.as_default():label_list=AGE_LISTifclass_type=='age'elseGENDER_LISTnlabels=len(label_list)model_fn=select_model(model_type)images=tf.placeholder(tf.float32,[None,RESIZE_FINAL,RESIZE_FINAL,3])logits=model_fn(nlabels,images,1,False)init=tf.global_variables_initializer()requested_step=requested_stepifrequested_stepelseNonecheckpoint_path='%s'%(model_dir)model_checkpoint_path,global_step=get_checkpoint(checkpoint_path,requested_step,checkpoint)saver=tf.train.Saver()saver.restore(sess,model_checkpoint_path)softmax_output=tf.nn.softmax(logits)coder=ImageCoder()def_is_png(filename):return'.png'in filenameapp=Flask(__name__)@app.route('/predictAge',methods=['GET','POST'])defprediction():start=time.time()imageUrl=request.values.get("imageUrl")#用户信息device=request.values.get("device")userid=request.values.get("userid")urlType=request.values.get("urlType")imageType=request.values.get("imageType")files=[]#支持本地图片和网络图片ifurlType=="local":filename=imageUrlelse:baseImageName=os.path.basename(imageUrl)filename="/home/hadoop/chongdianleme/ageimage/%s"%baseImageNamefilename=filename+imageTypeurllib.request.urlretrieve(imageUrl,filename)#通过我们前面讲的FaceNet里的人脸检测和对齐的http接口对图片进行处理,这样识别的年龄更精准url="http://172.17.100.216:8816/detectAndAlignedService"body_value={"image_file":filename,"alignedImage_files":filename}data_urlencode=urllib.parse.urlencode(body_value).encode(encoding='UTF8')request2=urllib.request.Request(url,data_urlencode)#调用接口resultJson=urllib.request.urlopen(request2).read().decode('UTF-8')#解析json格式的数据o=json.loads(resultJson)ts=o["times"]i=o["i"]newFileName=filename.replace(".","_0_.")files.append(newFileName)image_files=list(filter(lambda x:x is not None,[resolve_file(f)forf in files]))print(image_files)finalAge=0avgBest=0.00bestProb=0.00avgSecond=0.00secondProb=0.00forimage_file in image_files:try:print('Running file %s'%image_file)#image_batch = make_multi_crop_batch(image_file, coder)#startwith tf.gfile.FastGFile(filename,'rb')as f:image_data=f.read()# Convert any PNG to JPEG's for consistency.if_is_png(filename):print('Converting PNG to JPEG for %s'%filename)image_data=coder.png_to_jpeg(image_data)image=coder.decode_jpeg(image_data)crops=[]print('Running multi-cropped image')h=image.shape[0]w=image.shape[1]hl=h-RESIZE_FINALwl=w-RESIZE_FINALcrop=tf.image.resize_images(image,(RESIZE_FINAL,RESIZE_FINAL))crops.append(standardize_image(crop))crops.append(tf.image.flip_left_right(crop))corners=[(0,0),(0,wl),(hl,0),(hl,wl),(int(hl/2),int(wl/2))]forcorner in corners:ch,cw=cornercropped=tf.image.crop_to_bounding_box(image,ch,cw,RESIZE_FINAL,RESIZE_FINAL)crops.append(standardize_image(cropped))flipped=tf.image.flip_left_right(cropped)crops.append(standardize_image(flipped))image_batch=tf.stack(crops)#endbatch_results=sess.run(softmax_output,feed_dict={images:image_batch.eval(session=sess)})output=batch_results[0]batch_sz=batch_results.shape[0]fori inrange(1,batch_sz):output=output+batch_results[i]output/=batch_szbest=np.argmax(output)ageClass=label_list[best]#(25,32)bestAgeArr=ageClass.replace(" ","").replace("(","").replace(")","").split(",")#AGE_LIST = ['(0, 2)', '(4, 6)', '(8, 12)', '(15, 20)', '(25, 32)', '(38, 43)', '(48, 53)', '(60, 100)']#因为训练的模型用的是开源项目训练好的,是拿外国人的人脸数据训练,# 这样用来预测我们中国人的年龄会有一些差异,最好的方式是拿我们中国人自己的人脸年龄数据做训练,这样预测才会更好。# 所以针对外国人训练数据预测我们中国人需要对年龄做一些经验上的特殊处理ifint(bestAgeArr[1])==53:avgBest=56elifint(bestAgeArr[1])==43:avgBest=45elifint(bestAgeArr[1])==20:avgBest=22.66elifint(bestAgeArr[1])==6:avgBest=6.66else:avgBest=(int(bestAgeArr[0])+int(bestAgeArr[1]))/1.66bestProb=output[best]best_choice=(label_list[best],output[best])print('Guess @ 1 %s, prob = %.2f'%best_choice)nlabels=len(label_list)ifnlabels>2:output[best]=0second_best=np.argmax(output)secondAgeClass=label_list[second_best]#(25,32)secondAgeArr=secondAgeClass.replace(" ","").replace("(","").replace(")","").split(",")ifint(secondAgeArr[1])==53:avgSecond=56elifint(secondAgeArr[1])==43:avgSecond=45elifint(secondAgeArr[1])==20:avgSecond=22.66elifint(secondAgeArr[1])==6:avgSecond=6.66else:avgSecond=(int(secondAgeArr[0])+int(secondAgeArr[1]))/1.66secondProb=output[second_best]print('Guess @ 2 %s, prob = %.2f'%(label_list[second_best],output[second_best]))except Exception as e:import tracebacktraceback.print_exc()print('Failed to run image %s '%image_file)ifavgSecond>0:#基于加权平均法计算最终的合适的年龄finalAge=(avgBest*bestProb+avgSecond*secondProb)/(bestProb+secondProb)else:finalAge=avgBestprint("finalAge %s "%finalAge)end=time.time()times=str(end-start)#返回年龄等json格式数据result={"finalAge":finalAge,"times":times}out=json.dumps(result,ensure_ascii=False)print("out={0}".format(out))returnoutif__name__=='__main__':#指定ip地址和端口号app.run(host='172.17.100.216',port=8818)然后我们看下怎么部署和启动基于Flask的年龄识别服务,脚本代码如下所示:#创建shell脚本文件vim guessAgeService.sh#输入:python3 guessAgeWeb_chongdianleme.py#然后:wq保存#对guessAgeService.sh脚本授权可执行权限sudo chmod755guessAgeService.sh#然后再创建一个以后台方式运行的shell脚本:vim nohupguessAgeService.sh#输入:nohup/home/hadoop/chongdianleme/guessAgeService.sh>guessAge.log2>&1&#然后:wq保存#同样对nohupguessAgeService.sh脚本授权可执行权限sudo chmod755nohupguessAgeService.sh##2.读入数据<font color=#999AAA>代码如下(示例):```cdata=pd.read_csv('https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')print(data.head())
#最后运行sh nohupguessAgeService.sh脚本启动基于flask的人脸识别比对服务接口。
启动完成后,就可以在浏览器地址里输入url访问我们的服务了。这个http接口声明了同时支持get和post访问,我们在浏览器里输入地址就可以直接访问了。
http://172.17.100.216:8818/predictAge?imageUrl=/home/hadoop/chongdianleme/age/luhan2.jpg&urlType=local&imageType=jpg
这个就是一个接口服务,其他系统或者php、java web网站都可以调用这个接口,输入要预测imageUrl人脸图片路径,urlType是同时支持本地图片和网络图片链接的设置,返回人脸年龄json格式数据。
除了基于TensorFlow深度学习人脸识别源码级项目实战☞https://ke.qq.com/course/2554709?flowToken=1028991
其它深度学习框架也有不错的开源实现,比如MXNet,后面请大家关注充电了么app,课程,微信群,更多内容请看新书《分布式机器学习实战(人工智能科学与技术丛书)》
【新书介绍】
《分布式机器学习实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】https://item.jd.com/12743009.html
新书特色:深入浅出,逐步讲解分布式机器学习的框架及应用配套个性化推荐算法系统、人脸识别、对话机器人等实战项目
【新书介绍视频】
分布式机器学习实战(人工智能科学与技术丛书)新书【陈敬雷】https://ke.qq.com/course/3067704?flowToken=1029963
视频特色:重点对新书进行介绍,最新前沿技术热点剖析,技术职业规划建议!听完此课你对人工智能领域将有一个崭新的技术视野!职业发展也将有更加清晰的认识!
【精品课程】
《分布式机器学习实战》大数据人工智能AI专家级精品课程https://ke.qq.com/course/393750?flowToken=1028919
【免费体验视频】
人工智能百万年薪成长路线/从Python到最新热点技术 https://ke.qq.com/course/package/31251?flowToken=1029962
从Python编程零基础小白入门到人工智能高级实战系列课
https://ke.qq.com/course/package/29782?flowToken=1028733
视频特色:本系列专家级精品课有对应的配套书籍《分布式机器学习实战》,精品课和书籍可以互补式学习,彼此相互补充,大大提高了学习效率。本系列课和书籍是以分布式机器学习为主线,并对其依赖的大数据技术做了详细介绍,之后对目前主流的分布式机器学习框架和算法进行重点讲解,本系列课和书籍侧重实战,最后讲几个工业级的系统实战项目给大家。课程核心内容有互联网公司大数据和人工智能那些事、大数据算法系统架构、大数据基础、Python编程、Java编程、Scala编程、Docker容器、Mahout分布式机器学习平台、Spark分布式机器学习平台、分布式深度学习框架和神经网络算法、自然语言处理算法、工业级完整系统实战(推荐算法系统实战、人脸识别实战、对话机器人实战)、就业/面试技巧/职业生涯规划/职业晋升指导等内容。
【充电了么App】
本书在充电了么App里有对应的视频课程,更多学习资源也可以通过下载充电了么App客户端,也可以从各大应用商店里搜索“充电了么”自行下载。充电了么是专注上班族职业技能提升的在线教育平台。这里有海量免费课程,在这里你可以学习牛人的实际工作经验,也能够大幅提升职业技能,提高工作效率,带来经济效益!除了陈敬雷老师的课以外,还有上千万好课免费分享。全都在充电了么App上。充电了么APP是专注上班族职业培训充电学习的在线教育平台。各大安卓商店和苹果App Store搜索“充电了么”即可下载。按照下图输入网址也可以下载哦~
充电了么官网:http://www.chongdianleme.com/
充电了么App官网下载地址:https://a.app.qq.com/o/simple.jsp?pkgname=com.charged.app
功能特色如下:
【全行业职位】 - 专注职场上班族职业技能提升
覆盖所有行业和职位,不管你是上班族,高管,还是创业都有你要学习的视频和文章。其中大数据智能AI、区块链、深度学习是互联网一线工业级的实战经验。
除了专业技能学习,还有通用职场技能,比如企业管理、股权激励和设计、职业生涯规划、社交礼仪、沟通技巧、演讲技巧、开会技巧、发邮件技巧、工作压力如何放松、人脉关系等等,全方位提高你的专业水平和整体素质。
【牛人课堂】 - 学习牛人的工作经验
1.智能个性化引擎:
海量视频课程,覆盖所有行业、所有职位,通过不同行业职位的技能词偏好挖掘分析,智能匹配你目前职位最感兴趣的技能学习课程。
2.听课全网搜索
输入关键词搜索海量视频课程,应有尽有,总有适合你的课程。
3.听课播放详情
视频播放详情,除了播放当前视频,更有相关视频课程和文章阅读,对某个技能知识点强化,让你轻松成为某个领域的资深专家。
【精品阅读】 - 技能文章兴趣阅读
1.个性化阅读引擎:
千万级文章阅读,覆盖所有行业、所有职位,通过不同行业职位的技能词偏好挖掘分析,智能匹配你目前职位最感兴趣的技能学习文章。
2.阅读全网搜索
输入关键词搜索海量文章阅读,应有尽有,总有你感兴趣的技能学习文章。
【机器人老师】 - 个人提升趣味学习
基于搜索引擎和智能深度学习训练,为您打造更懂你的机器人老师,用自然语言和机器人老师聊天学习,寓教于乐,高效学习,快乐人生。
【精短课程】 - 高效学习知识
海量精短牛人课程,满足你的时间碎片化学习,快速提高某个技能知识点。