基於深度學習的PM2.5實時預測系統開發

尊敬的讀者您好:筆者很高興自己的文章能被閱讀,但原創與編輯均不易,所以轉載請必須註明本文出處並附上本文地址超鏈接以及博主博客地址:https://blog.csdn.net/vensmallzeng。若覺得本文對您有益處還請幫忙點個贊鼓勵一下,筆者在此感謝每一位讀者,如需聯繫筆者,請記下郵箱:[email protected],謝謝合作!

 

 

 

今天去首師大參加了農行軟開筆試,時間2小時,題目分爲四大類型,總體感覺難度不大但是很多題目都觸碰到了我的知識盲區,所以還是得加強基礎知識的學習,果真應驗了那句話"基礎不牢,地動山搖"啊!這次想把我申請的那個科技基金項目好好總結一下,該科技基金是學校設立用於鼓勵大家崇尚科研,學會寫本子的,每年好像資助100項左右且資助額度是4000RMB/年,感覺學校在這一點做的很好,給學校點個大大的贊。言歸正傳,下面就自己是如何將科技基金順利結題的做一個小小的總結。

一、數據集建立

圖片的來源:http://www.tour-beijing.com/real_time_weather_photo/

圖片對應的PM2.5來源:http://aqicn.org/city/beijing/us-embassy/cn/

整理收集到的圖片與相應的PM2.5,得到一個共包含幾萬個樣本的數據集,且每個樣本的以“序號+PM2.5濃度值”命名。具體數據集如下圖所示

 

二、預測模型建立

考慮到所建立數據集的規模不足以訓練好一個VGG16的網絡,所以經過相關文獻發現可以通過“遷移學習”的方式解決這個問題。大體思路分兩步,第一步:復現在ImageNet數據集上訓練好的VGG16網絡,第二步:基於遷移學習思想採用訓練數據集對VGG16進行微調。

第一步的目的在於要弄懂VGG16的網絡結構長啥樣,下圖是論文作者給出的一張網絡結構描述表。

下圖是真實的網絡結構:

 

第二步是要基於原始的VGG16網絡,通過微調最後一層全連接層參數來實現遷移學習。在微調之前,需要先下載訓練好的vgg16.npy文件,如果您還沒有下載請轉向“https://pan.baidu.com/s/1Spps1Wy0bvrQHH2IMkRfpg”。下面就來分析一下基於遷移學習的VGG16網絡源碼。

1、導入需要的包

import os
import numpy as np
import tensorflow as tf
import skimage.io
import skimage.transform
import matplotlib.pyplot as plt

2、讀取圖片並進行resize操作

# 讀取圖片
def load_data():
    imgs = {'training': []}
    fpaths = []
    labels = []
    for k in imgs.keys():
        dir = 'C:/PycharmFiles/Tensorflow_VGG_PM2.5/for_transfer_learning/data/' + k
        for file in os.listdir(dir):
            fpath = os.path.join(dir, file)
            fpaths.append(fpath)
            if not file.lower().endswith('.jpg'):
                continue
            try:
                resized_img = load_img(os.path.join(dir, file))
            except OSError:
                continue
            imgs[k].append(resized_img)  # [1, height, width, depth] * n
            if len(imgs[k]) == 400:  # only use 400 imgs to reduce my memory load
                break
    # fake length data for tiger and cat
            label = int(file.split("_")[0])
            labels.append(label)
    ys = [[label] for label in labels]
    ys1 = np.array(ys)
    xs = np.concatenate(imgs['training'], axis=0)
   
    return xs, ys1

#對圖片進行resize操作
def load_img(path):
    img = skimage.io.imread(path)
    img = img / 255.0
    # print "Original Image Shape: ", img.shape
    # we crop image from center
    short_edge = min(img.shape[:2])
    yy = int((img.shape[0] - short_edge) / 2)
    xx = int((img.shape[1] - short_edge) / 2)
    crop_img = img[yy: yy + short_edge, xx: xx + short_edge]
    # resize to 224, 224
    resized_img = skimage.transform.resize(crop_img, (224, 224))[None, :, :, :]  # shape [1, 224, 224, 3]
    return resized_img

3、微調VGG,只對後面的全連接層進行訓練

class Vgg16:
    vgg_mean = [103.939, 116.779, 123.68]
    print(3)

    def __init__(self, vgg16_npy_path=None, restore_from=None):
        # pre-trained parameters
        try:
            self.data_dict = np.load(vgg16_npy_path, encoding='latin1', allow_pickle = True).item()
            print(Vgg16)
        except FileNotFoundError:
            print(
                'Please download VGG16 parameters from here https://mega.nz/#!YU1FWJrA!O1ywiCS2IiOlUCtCpI6HTJOMrneN-Qdv3ywQP5poecM\nOr from my Baidu Cloud: https://pan.baidu.com/s/1Spps1Wy0bvrQHH2IMkRfpg')

        self.tfx = tf.placeholder(tf.float32, [None, 224, 224, 3])
        self.tfy = tf.placeholder(tf.float32, [None, 1])

        # Convert RGB to BGR
        red, green, blue = tf.split(axis=3, num_or_size_splits=3, value=self.tfx * 255.0)
        bgr = tf.concat(axis=3, values=[
            blue - self.vgg_mean[0],
            green - self.vgg_mean[1],
            red - self.vgg_mean[2],
        ])

        # pre-trained VGG layers are fixed in fine-tune
        conv1_1 = self.conv_layer(bgr, "conv1_1")
        conv1_2 = self.conv_layer(conv1_1, "conv1_2")
        pool1 = self.max_pool(conv1_2, 'pool1')

        conv2_1 = self.conv_layer(pool1, "conv2_1")
        conv2_2 = self.conv_layer(conv2_1, "conv2_2")
        pool2 = self.max_pool(conv2_2, 'pool2')

        conv3_1 = self.conv_layer(pool2, "conv3_1")
        conv3_2 = self.conv_layer(conv3_1, "conv3_2")
        conv3_3 = self.conv_layer(conv3_2, "conv3_3")
        pool3 = self.max_pool(conv3_3, 'pool3')

        conv4_1 = self.conv_layer(pool3, "conv4_1")
        conv4_2 = self.conv_layer(conv4_1, "conv4_2")
        conv4_3 = self.conv_layer(conv4_2, "conv4_3")
        pool4 = self.max_pool(conv4_3, 'pool4')

        conv5_1 = self.conv_layer(pool4, "conv5_1")
        conv5_2 = self.conv_layer(conv5_1, "conv5_2")
        conv5_3 = self.conv_layer(conv5_2, "conv5_3")
        pool5 = self.max_pool(conv5_3, 'pool5')

        # detach original VGG fc layers and
        # reconstruct your own fc layers serve for your own purpose
        # flatten
        self.flatten = tf.reshape(pool5, [-1, 7 * 7 * 512])

        # fully connected
        self.fc6 = tf.layers.dense(self.flatten, 256, tf.nn.relu, name='fc6')
        self.out = tf.layers.dense(self.fc6, 1, name='out')

        self.sess = tf.Session()
        if restore_from:
            saver = tf.train.Saver()
            saver.restore(self.sess, restore_from)
        else:  # training graph
            self.loss = tf.losses.mean_squared_error(labels=self.tfy, predictions=self.out)
            self.train_op = tf.train.RMSPropOptimizer(0.001).minimize(self.loss)
            self.sess.run(tf.global_variables_initializer())

    def max_pool(self, bottom, name):
        return tf.nn.max_pool(bottom, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

    def conv_layer(self, bottom, name):
        with tf.variable_scope(name):  # CNN's filter is constant, NOT Variable that can be trained
            conv = tf.nn.conv2d(bottom, self.data_dict[name][0], [1, 1, 1, 1], padding='SAME')
            lout = tf.nn.relu(tf.nn.bias_add(conv, self.data_dict[name][1]))
            return lout

    def train(self, x, y):
        loss, _ = self.sess.run([self.loss, self.train_op], {self.tfx: x, self.tfy: y})
        return loss

    def predict(self, paths):
        fig, axs = plt.subplots(1, 2)
        for i, path in enumerate(paths):
            x = load_img(path)
            length = self.sess.run(self.out, {self.tfx: x})
            print("%s's prediction results: %s" % (path,length))
            

    def save(self, path='C:/PycharmFiles/Tensorflow_VGG_PM2.5/for_transfer_learning/model/transfer_learn'):
        saver = tf.train.Saver()
        saver.save(self.sess, path, write_meta_graph=False)

4、進行訓練,定義 epoch 爲1000, batchsize爲10,並打印輸出每個 epoch的 loss 值。 

def train():
    # tigers_x, cats_x, tigers_y, cats_y = load_data()
    xs, ys = load_data()
    print(1)
    vgg = Vgg16(vgg16_npy_path='C:/PycharmFiles/Tensorflow_VGG_PM2.5/for_transfer_learning/vgg16.npy')
    print(2)
    print('Net built')


    for i in range(1000):
        b_idx = np.random.randint(0, len(xs), 10)
        train_loss = vgg.train(xs[b_idx], ys[b_idx])
        print(i, 'train loss: ', train_loss)
    vgg.save('C:/PycharmFiles/Tensorflow_VGG_PM2.5/for_transfer_learning/model/transfer_learn')  # save learned fc layers

5、基於訓練好的模型進行預測,輸入一張任意大小圖片,輸出一個預測的PM2.5值。

def eval():
    vgg = Vgg16(vgg16_npy_path='C:/PycharmFiles/Tensorflow_VGG_PM2.5/for_transfer_learning/vgg16.npy',
                restore_from='C:/PycharmFiles/Tensorflow_VGG_PM2.5/for_transfer_learning/model/transfer_learn')
    fpaths = []

    dir = 'C:/PycharmFiles/Tensorflow_VGG_PM2.5/for_transfer_learning/data/test'
    for file in os.listdir(dir):
        fpath = os.path.join(dir, file)
        fpaths.append(fpath)
    vgg.predict( fpaths )

三、模型遷移移動端

剛開始將tensorflow模型遷移到移動端,參考了很多網上的教程的,也走了很多彎路,但最終還是將訓練效果最好的VGG16網絡成功遷移到Android Studio的移動客戶端上了,實現了通過手機對周圍環境進行拍照即可預測周圍PM2.5濃度功能。這部分考慮到與自己的碩士畢業論文掛鉤,所以在未畢業之前暫時不能對外公佈,但可以給大家一個博客“https://blog.csdn.net/u012328159/article/details/81123018”參考參考,讓大家少走一些彎路,還請各位多多包涵。

 

 

 

日積月累,與君共進,增增小結,未完待續。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章