教程 | 如何用百度深度學習框架PaddlePaddle做數據預處理

2021-02-15 機器之心

本文主要介紹了百度的深度學習開源框架PaddlePaddle的數據預處理過程,創建一個reader讀取數據,一行代碼搞定數據的輸入、混洗和批量讀取。本文作者胡曉曼是一名高級算法工程師,熱衷寫通俗易懂的深度學習入門文章。

PaddlePaddle 的基本數據格式

根據官網的資料,總結出 PaddlePaddle 支持多種不同的數據格式,包括四種數據類型和三種序列格式:

四種數據類型:

api 如下:

dim(int) 向量維度

seq_type(int) 輸入的序列格式

seq_type(int):輸入的序列格式

value_range(int):每個元素的範圍

三種序列格式:

SequenceType.NO_SEQUENCE:不是一條序列

SequenceType.SEQUENCE:是一條時間序列

SequenceType.SUB_SEQUENCE: 是一條時間序列,且序列的每一個元素還是一個時間序列。

api 如下:

說明:稠密向量的序列格式

參數:dim(int):稠密向量的維度

返回類型:InputType

dim(int):稀疏向量的維度

seq_type(int):輸入的序列格式

dim(int):稀疏向量的維度

seq_type(int):輸入的序列格式

不同的數據類型和序列模式返回的格式不同,如下表:

其中 f 表示浮點數,i 表示整數

注意:對 sparse_binary_vector 和 sparse_float_vector,PaddlePaddle 存的是有值位置的索引。例如,

對一個 5 維非序列的稀疏 01 向量 [0, 1, 1, 0, 0],類型是 sparse_binary_vector,返回的是 [1, 2]。(因為只有第 1 位和第 2 位有值)

對一個 5 維非序列的稀疏浮點向量 [0, 0.5, 0.7, 0, 0],類型是 sparse_float_vector,返回的是 [(1, 0.5), (2, 0.7)]。(因為只有第一位和第二位有值,分別是 0.5 和 0.7)

PaddlePaddle 的數據讀取方式

我們了解了上文的四種基本數據格式和三種序列模式後,在處理自己的數據時可以根據需求選擇,但是處理完數據後如何把數據放到模型裡去訓練呢?我們知道,基本的方法一般有兩種:

在 PaddlePaddle 中我們可以有三種模式來讀取數據:分別是 reader、reader creator 和 reader decorator, 這三者有什麼區別呢?

reader:從本地、網絡、分布式文件系統 HDFS 等讀取數據,也可隨機生成數據,並返回一個或多個數據項。

reader creator:一個返回 reader 的函數。

reader decorator:裝飾器,可組合一個或多個 reader。

Reader

我們先以 reader 為例,為房價數據(斯坦福吳恩達的公開課第一課舉例的數據)創建一個 reader:

1. 創建一個 reader,實質上是一個迭代器,每次返回一條數據(此處以房價數據為例)

reader = paddle.dataset.uci_housing.train()

2. 創建一個 shuffle_reader,把上一步的 reader 放進去,配置 buf_size 就可以讀取 buf_size 大小的數據自動做 shuffle,讓數據打亂,隨機化

shuffle_reader = paddle.reader.shuffle(reader,buf_size= 100)

3. 創建一個 batch_reader,把上一步混洗好的 shuffle_reader 放進去,給定 batch_size,即可創建。

batch_reader = paddle.batch(shuffle_reader,batch_size = 2)

這三種方式也可以組合起來放一塊:

reader = paddle.batch(

   paddle.reader.shuffle(

       uci_housing.train(),

   buf_size = 100),

   batch_size=2)    

可以以一個直觀的圖來表示:

從圖中可以看到,我們可以直接從原始數據集裡拿去數據,用 reader 讀取,一條條灌倒 shuffle_reader 裡,在本地隨機化,把數據打亂,做 shuffle,然後把 shuffle 後的數據,一個 batch 一個 batch 的形式,批量的放到訓練器裡去進行每一步的迭代和訓練。流程簡單,而且只需要使用一行代碼即可實現整個過程。

Reader creator

如果想要生成一個簡單的隨機數據,以 reader creator 為例:

def reader_creator():

   def reader():

       while True:

           yield numpy.random.uniform(-1,1,size=784)

   return reader

源碼見 creator.py, 支持四種格式:np_array,text_file,RecordIO 和 cloud_reader

Code:https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/reader/creator.py

 

Reader decorator

如果想要讀取同時讀取兩部分的數據,那麼可以定義兩個 reader,合併後對其進行 shuffle。如我想讀取所有用戶對比車系的數據和瀏覽車系的數據,可以定義兩個 reader,分別為 contrast() 和 view(),然後通過預定義的 reader decorator 緩存並組合這些數據,在對合併後的數據進行亂序操作。源碼見 decorator.py

data = paddle.reader.shuffle(

       paddle.reader.compose(

           paddle.reader(contradt(contrast_path),buf_size = 100),

           paddle.reader(view(view_path),buf_size = 200),

           500)

這樣有一個很大的好處,就是組合特徵來訓練變得更容易了!傳統的跑模型的方法是,確定 label 和 feature,儘可能多的找合適的 feature 扔到模型裡去訓練,這樣我們就需要做一張大表,訓練完後我們可以分析某些特徵的重要性然後重新增加或減少一些 feature 來進行訓練,這樣我們有需要對原來的 label-feature 表進行修改,如果數據量小沒啥影響,就是麻煩點,但是數據量大的話需要每一次增加 feature,和主鍵、label 來 join 的操作都會很耗時,如果採取這種方式的話,我們可以對某些同一類的特徵做成一張表,數據存放的地址存為一個變量名,每次跑模型的時候想選取幾類特徵,就創建幾個 reader,用 reader decorator 組合起來,最後再 shuffle 灌倒模型裡去訓練。這!樣!是!不!是!很!方!便!

如果沒理解,我舉一個實例,假設我們要預測用戶是否會買車,label 是買車 or 不買車,feature 有瀏覽車系、對比車系、關注車系的功能偏好等等 20 個,傳統的思維是做成這樣一張表:

如果想要減少 feature_2, 看看 feature_2 對模型的準確率影響是否很大,那麼我們需要在這張表裡去掉這一列,想要增加一個 feature 的話,也需要在 feature 裡增加一列,如果用 reador decorator 的話,我們可以這樣做數據集:

把相同類型的 feature 放在一起,不用頻繁的 join 減少時間,一共做四個表,創建 4 個 reador:

data = paddle.reader.shuffle(

           paddle.reader.compose(

               paddle.reader(table1(table1_path),buf_size = 100),

               paddle.reader(table2(table2_path),buf_size = 100),

               paddle.reader(table3(table3_path),buf_size = 100),

               paddle.reader(table4(table4_path),buf_size = 100),

           500)

如果新發現了一個特徵,想嘗試這個特徵對模型提高準確率有沒有用,可以再單獨把這個特徵數據提取出來,再增加一個 reader,用 reader decorator 組合起來,shuffle 後放入模型裡跑就行了。

PaddlePaddle 的數據預處理實例

還是以手寫數字為例,對數據進行處理後並劃分 train 和 test,只需要 4 步即可:

1. 指定數據地址

import paddle.v2.dataset.common

import subprocess

import numpy

import platform

__all__ = ['train', 'test', 'convert']

URL_PREFIX = 'http://yann.lecun.com/exdb/mnist/'

TEST_IMAGE_URL = URL_PREFIX + 't10k-images-idx3-ubyte.gz'

TEST_IMAGE_MD5 = '9fb629c4189551a2d022fa330f9573f3'

TEST_LABEL_URL = URL_PREFIX + 't10k-labels-idx1-ubyte.gz'

TEST_LABEL_MD5 = 'ec29112dd5afa0611ce80d1b7f02629c'

TRAIN_IMAGE_URL = URL_PREFIX + 'train-images-idx3-ubyte.gz'

TRAIN_IMAGE_MD5 = 'f68b3c2dcbeaaa9fbdd348bbdeb94873'

TRAIN_LABEL_URL = URL_PREFIX + 'train-labels-idx1-ubyte.gz'

TRAIN_LABEL_MD5 = 'd53e105ee54ea40749a09fcbcd1e9432'

2. 創建 reader creator

def reader_creator(image_filename, label_filename, buffer_size):

    # 創建一個reader

    def reader():

        if platform.system() == 'Darwin':

            zcat_cmd = 'gzcat'

        elif platform.system() == 'Linux':

            zcat_cmd = 'zcat'

        else:

            raise NotImplementedError()

        m = subprocess.Popen([zcat_cmd, image_filename], stdout=subprocess.PIPE)

        m.stdout.read(16)  

        l = subprocess.Popen([zcat_cmd, label_filename], stdout=subprocess.PIPE)

        l.stdout.read(8)  

        try:  # reader could be break.

            while True:

                labels = numpy.fromfile(

                    l.stdout, 'ubyte', count=buffer_size).astype("int")

                if labels.size != buffer_size:

                    break  # numpy.fromfile returns empty slice after EOF.

                images = numpy.fromfile(

                    m.stdout, 'ubyte', count=buffer_size * 28 * 28).reshape(

                        (buffer_size, 28 * 28)).astype('float32')

                images = images / 255.0 * 2.0 - 1.0

                for i in xrange(buffer_size):

                    yield images[i, :], int(labels[i])

        finally:

            m.terminate()

            l.terminate()

    return reader

3. 創建訓練集和測試集

def train():

    """

    創建mnsit的訓練集 reader creator

    返回一個reador creator,每個reader裡的樣本都是圖片的像素值,在區間[0,1]內,label為0~9

    返回:training reader creator

    """

    return reader_creator(

        paddle.v2.dataset.common.download(TRAIN_IMAGE_URL, 'mnist',

                                          TRAIN_IMAGE_MD5),

        paddle.v2.dataset.common.download(TRAIN_LABEL_URL, 'mnist',

                                          TRAIN_LABEL_MD5), 100)

def test():

    """

    創建mnsit的測試集 reader creator

    返回一個reador creator,每個reader裡的樣本都是圖片的像素值,在區間[0,1]內,label為0~9

    返回:testreader creator

    """

    return reader_creator(

        paddle.v2.dataset.common.download(TEST_IMAGE_URL, 'mnist',

                                          TEST_IMAGE_MD5),

        paddle.v2.dataset.common.download(TEST_LABEL_URL, 'mnist',

                                          TEST_LABEL_MD5), 100)

4. 下載數據並轉換成相應格式

def fetch():

    paddle.v2.dataset.common.download(TRAIN_IMAGE_URL, 'mnist', TRAIN_IMAGE_MD5)

    paddle.v2.dataset.common.download(TRAIN_LABEL_URL, 'mnist', TRAIN_LABEL_MD5)

    paddle.v2.dataset.common.download(TEST_IMAGE_URL, 'mnist', TEST_IMAGE_MD5)

    paddle.v2.dataset.common.download(TEST_LABEL_URL, 'mnist', TRAIN_LABEL_MD5)

def convert(path):

    """

    將數據格式轉換為 recordio format

    """

    paddle.v2.dataset.common.convert(path, train(), 1000, "minist_train")

    paddle.v2.dataset.common.convert(path, test(), 1000, "minist_test")

如果想換成自己的訓練數據,只需要按照步驟改成自己的數據地址,創建相應的 reader creator(或者 reader decorator)即可。

這是圖像的例子,如果我們想訓練一個文本模型,做一個情感分析,這個時候如何處理數據呢?步驟也很簡單。

假設我們有一堆數據,每一行為一條樣本,以 \t 分隔,第一列是類別標籤,第二列是輸入文本的內容,文本內容中的詞語以空格分隔。以下是兩條示例數據:

positive        今天終於試了自己理想的車 外觀太騷氣了 而且中控也很棒

negative       這臺車好貴 而且還費油 性價比太低了

現在開始做數據預處理

1. 創建 reader

def train_reader(data_dir, word_dict, label_dict):

    def reader():

        UNK_ID = word_dict["<UNK>"]

        word_col = 0

        lbl_col = 1

        for file_name in os.listdir(data_dir):

            with open(os.path.join(data_dir, file_name), "r") as f:

                for line in f:

                    line_split = line.strip().split("\t")

                    word_ids = [

                        word_dict.get(w, UNK_ID)

                        for w in line_split[word_col].split()

                    ]

                    yield word_ids, label_dict[line_split[lbl_col]]

    return reader

返回類型為: paddle.data_type.integer_value_sequence(詞語在字典的序號)和 paddle.data_type.integer_value(類別標籤)

2. 組合讀取方式

train_reader = paddle.batch(

        paddle.reader.shuffle(

            reader.train_reader(train_data_dir, word_dict, lbl_dict),

            buf_size=1000),

        batch_size=batch_size)

完整的代碼如下(加上了劃分 train 和 test 部分):

import os

def train_reader(data_dir, word_dict, label_dict):

    """

   創建訓練數據reader

    :param data_dir: 數據地址.

    :type data_dir: str

    :param word_dict: 詞典地址,

        詞典裡必須有 "UNK" .

    :type word_dict:python dict

    :param label_dict: label 字典的地址

    :type label_dict: Python dict

    """

    def reader():

        UNK_ID = word_dict["<UNK>"]

        word_col = 1

        lbl_col = 0

        for file_name in os.listdir(data_dir):

            with open(os.path.join(data_dir, file_name), "r") as f:

                for line in f:

                    line_split = line.strip().split("\t")

                    word_ids = [

                        word_dict.get(w, UNK_ID)

                        for w in line_split[word_col].split()

                    ]

                    yield word_ids, label_dict[line_split[lbl_col]]

    return reader

def test_reader(data_dir, word_dict):

    """

    創建測試數據reader

    :param data_dir: 數據地址.

    :type data_dir: str

    :param word_dict: 詞典地址,

        詞典裡必須有 "UNK" .

    :type word_dict:python dict

    """

    def reader():

        UNK_ID = word_dict["<UNK>"]

        word_col = 1

        for file_name in os.listdir(data_dir):

            with open(os.path.join(data_dir, file_name), "r") as f:

                for line in f:

                    line_split = line.strip().split("\t")

                    if len(line_split) < word_col: continue

                    word_ids = [

                        word_dict.get(w, UNK_ID)

                        for w in line_split[word_col].split()

                    ]

                    yield word_ids, line_split[word_col]

    return reader

總結

這篇文章主要講了在 paddlepaddle 裡如何加載自己的數據集,轉換成相應的格式,並劃分 train 和 test。我們在使用一個框架的時候通常會先去跑幾個簡單的 demo,但是如果不用常見的 demo 的數據,自己做一個實際的項目,完整的跑通一個模型,這才代表我們掌握了這個框架的基本應用知識。跑一個模型第一步就是數據預處理,在 paddlepaddle 裡,提供的方式非常簡單,但是有很多優點:

而我之前使用過 mxnet 來訓練車牌識別的模型,50w 的圖片數據想要一次訓練是非常慢的,這樣的話就有兩個解決方法:一是批量訓練,這一點大多數的框架都會有,二是轉換成 mxnet 特有的 rec 格式,提高讀取效率,可以通過 im2rec.py 將圖片轉換,比較麻煩,如果是 tesnorflow,也有相對應的特定格式 tfrecord,這幾種方式各有優劣,從易用性上,paddlepaddle 是比較簡單的。 

參考文章:

1. 官網說明:http://doc.paddlepaddle.org/develop/doc_cn/getstarted/concepts/use_concepts_cn.html

本文為機器之心專欄,轉載請聯繫原作者獲得授權

✄---

加入機器之心(全職記者/實習生):hr@jiqizhixin.com

投稿或尋求報導:content@jiqizhixin.com

廣告&商務合作:bd@jiqizhixin.com

相關焦點

  • 教程| 如何用百度深度學習框架PaddlePaddle做數據預處理
    機器之心經授權轉載 作者:胡曉曼 本文主要介紹了百度的深度學習開源框架PaddlePaddle的數據預處理過程,創建一個reader讀取數據,一行代碼搞定數據的輸入、混洗和批量讀取。本文作者胡曉曼是一名高級算法工程師,熱衷寫通俗易懂的深度學習入門文章。
  • 【深度學習系列】關於PaddlePaddle的一些避「坑」技巧
    ,熟悉Tensorflow,PaddlePaddle等深度學習框架,負責過多個機器學習落地項目,如垃圾評論自動過濾,用戶分級精準營銷,分布式深度學習平臺搭建等,都取了的不錯的效果。博客專欄:https://www.cnblogs.com/charlotte77/前文傳送門:【好書推薦&學習階段】三個月教你從零入門深度學習【深度學習系列】PaddlePaddle之手寫數字識別【深度學習系列】卷積神經網絡CNN原理詳解(一)——基本原理【深度學習系列】PaddlePaddle之數據預處理
  • 深度學習系列:PaddlePaddle之手寫數字識別
    上周在搜索關於深度學習分布式運行方式的資料時,無意間搜到了paddlepaddle,發現這個框架的分布式訓練方案做的還挺不錯的,想跟大家分享一下。不過呢,這塊內容太複雜了,所以就簡單的介紹一下paddlepaddle的第一個「hello word」程序----mnist手寫數字識別。下一次再介紹用PaddlePaddle做分布式訓練的方案。
  • PaddlePaddle深度學習開源平臺:等待眾人划槳的中國AI大船
    一、 PaddlePaddle是什麼 我們說了很多歷史,也提到了一些舞臺上正興的機器學習平臺,而這篇文字,是想介紹一下一個新的選擇:來自百度的paddlepaddle。Paddle(Parallel Distributed Deep Learning,並行分布式深度學習)。
  • 基於PaddlePaddle的詞向量實戰 | 深度學習基礎任務教程系列
    但是基於神經網絡的模型不需要計算和存儲一個在全語料上統計產生的大表,而是通過學習語義信息得到詞向量,因此能很好地解決以上問題。  本教程旨在展示神經網絡訓練詞向量的細節,以及如何用 PaddlePaddle 訓練一個詞向量模型。
  • 基於PaddlePaddle的機器翻譯教程 | 深度學習基礎任務系列
    近年來,深度學習技術的發展為解決上述挑戰提供了新的思路。將深度學習應用於機器翻譯任務的方法大致分為兩類:仍以統計機器翻譯系統為框架,只是利用神經網絡來改進其中的關鍵模塊,如語言模型、調序模型等(見圖1的左半部分);不再以統計機器翻譯系統為框架,而是直接用神經網絡將源語言映射到目標語言,即端到端的神經網絡機器翻譯(End-to-EndNeuralMachineTranslation
  • 基於PaddlePaddle的詞向量實戰 | 深度學習基礎任務教程系列(二)
    而基於神經網絡的模型不需要計算和存儲一個在全語料上統計產生的大表,是通過學習語義信息得到詞向量,因此能很好地解決以上問題。本教程旨在展示神經網絡訓練詞向量的細節,以及如何用PaddlePaddle訓練一個詞向量模型。
  • 十行代碼就能搞定深度學習?飛槳框架高層API,一起輕鬆玩轉AI
    機器之心發布機器之心編輯部嚮往深度學習技術,可是深度學習框架太難學怎麼辦?百度傾心打造飛槳框架高層 API,零基礎也能輕鬆上手深度學習,一起來看看吧?為了簡化深度學習的學習過程、降低深度學習的開發難度,百度飛槳框架歷經近一年的打磨,不斷地優化深度學習 API,並針對開發者的使用場景進行封裝,在飛槳框架的最新版本中,推出了高低融合、科學統一的飛槳全新 API 體系。飛槳框架將 API 分為兩種,基礎 API 和高層 API。
  • 【深度學習系列】用PaddlePaddle和Tensorflow實現經典CNN網絡AlexNet
    ,熟悉Tensorflow,PaddlePaddle等深度學習框架,負責過多個機器學習落地項目,如垃圾評論自動過濾,用戶分級精準營銷,分布式深度學習平臺搭建等,都取了的不錯的效果。博客專欄:https://www.cnblogs.com/charlotte77/前文傳送門:【好書推薦&學習階段】三個月教你從零入門深度學習【深度學習系列】PaddlePaddle之手寫數字識別【深度學習系列】卷積神經網絡CNN原理詳解(一)——基本原理【深度學習系列】PaddlePaddle之數據預處理
  • PaddlePaddle入門:從對話系統中的情感分析談起
    人工智慧時代,各種深度學習框架大行其道,掌握一種框架已經成為這個時代的算法工程師標配。
  • 【深度學習系列】用PaddlePaddle和Tensorflow實現經典CNN網絡GoogLeNet
    點擊上圖,立即開啟AI急速修煉作者:Charlotte    高級算法工程師 ,博客專家;擅長用通俗易懂的方式講解深度學習和機器學習算法
  • PaddlePaddle升級解讀 | PaddleHub輕鬆完成遷移學習
    遷移學習(Transfer Learning)是屬於深度學習的一個子研究領域,該研究領域的目標在於利用數據、任務、或模型之間的相似性,將在舊領域學習過的知識,遷移應用於新領域中。遷移學習吸引了很多研究者投身其中,因為它能夠很好的解決深度學習中的以下幾個問題:為了讓開發者更便捷地應用遷移學習,百度PaddlePaddle開源了預訓練模型管理工具PaddleHub。開發者用使用僅僅十餘行的代碼,就能完成遷移學習。本文將為讀者全面介紹PaddleHub並介紹其應用方法。
  • 深度學習與PaddlePaddle的應用-個性化推薦
    作者:沈克強 來源:人工智慧學習圈I 研究背景推薦系統(Recommender System)是向用戶建議有用物品的軟體工具和技術,它運用數據分析、數據挖掘等技術,實現對用戶瀏覽信息或商品進行智能推薦,是機器學習,尤其是深度學習算法的重要應用場景。
  • 全面支持三大主流環境 |百度PaddlePaddle新增Windows環境支持
    引言:PaddlePaddle作為國內首個深度學習框架,最近發布了更加強大的Fluid1.2版本, 增加了對windows環境的支持,全面支持了Linux、Mac、 windows三大環境。一、安裝在深度學習框架上, python語言由於本身的易用性和豐富的類庫,被眾多深度學習框架作為了應用方面的首選,PaddlePaddle也將python語言作為了自己的應用語言,如下介紹在python下怎麼安裝PaddlePaddle。1.
  • 基於PaddlePaddle的圖片分類實戰 | 深度學習基礎任務教程系列
    一般來說,圖像分類通過手工提取特徵或特徵學習方法對整個圖像進行全部描述,然後使用分類器判別物體類別,因此如何提取圖像的特徵至關重要。基於深度學習的圖像分類方法,可以通過有監督或無監督的方式學習層次化的特徵描述,從而取代了手工設計或選擇圖像特徵的工作。
  • 一行命令啟動,十分鐘完成部署,PaddleServing開放模型即服務功能
    相信在深度學習領域中,不少做算法的小夥伴都會對這句話產生共鳴。辛辛苦苦搭建好網絡,望眼欲穿得訓練調試好模型,等到最後要部署,面對紛繁複雜的實際部署環境時,才發現原來終極大魔王在這裡!當然這個魔王不會喊打喊殺,但他會給你出難題,情景可能是這樣的:在此緊要關頭,是否有什麼捷徑幫助小夥伴順利通關呢?
  • 如何評價百度剛剛開源的Paddle平臺?
    但今天開源的Paddle當然不是3年前的簡單模型,3年前Paddle可能還是一個獨立的深度學習平臺,不能很好地支持把數據從其他平臺接入的需求。但今天的Paddle已經在處處強調,它的特色是讓Spark與PADDLE耦合起來,是一個基於Spark的異構分布式深度學習系統。
  • 乘風破浪的Paddle之LSTM
    作者:劉凱旋 來源:人工智慧學習圈本文未經授權,嚴禁轉載,轉載請聯繫作者本人。自然語言處理(NLP)主要是研究實現人與計算機之間用自然語言進行有效通信的各種理論和方法。基於神經網絡的深度學習技術具有強大的表達能力、端到端解決問題的能力,因而在NLP任務的應用上越來越廣泛和有效。
  • 重磅| PaddlePaddle中文文檔強勢來襲!
    在發布三年之後,百度深度學習框架PaddlePaddle有了官方中文版文檔。今年11月份,PaddlePaddle的用戶們在中文社區論壇以及社群上發出召喚:PaddlePaddle官方是否能做個PaddlePaddle文檔的中文版?這個呼聲在PaddlePaddle團隊內引起廣泛關注,研發團隊馬上在 GitHub 上展開了一個 PaddlePaddle中文文檔項目。
  • 深度學習CPU加速方案,飛槳全流程開發工具PaddleX 打通OpenVINO...
    現如今,深度學習技術為工業質檢、安防巡檢、金融、醫療、能源等各行各業降本增效的效果已被廣泛驗證。然而,深度學習算法往往需要較高性能的計算晶片,以滿足大計算量、高推理速度等產業需求。這帶來一個問題,如果對前期大批量投入的低性能設備進行升級甚至更換,都將是一筆非常大的費用,從而提升了產業智能化升級時前期投入的硬體成本。