ABP (.Net Core 3.1版本) 使用MySQL數據庫遷移啟動模板項目(1)

最近要搭建新項目,因為還沒有用過.net core,所以想用.net core的環境搭建新項目,因為不熟悉.net core的架構,所以就下載了abp項目先了解一下。
但是自己太菜了,下載了模板項目,在啟動的過程中一波三折,其曲折真是無法用言語形容。(但是我沒有灰心!沒有什麼技術是在努力的情況下學不會的,是嘛)
在搜集信息的過程中,很多網友分享的資料都很有幫助,但是有一個問題的方法對於我這邊遇到的問題卻用不上,因為問題沒有得到解決。但是卻給了我不同的思路,從而讓問題得到解決,模板項目終於順利啟動。

先貼上我參考的帖子,正是因為有優秀的網友的分享,我才打開了abp模板項目的大門!(不多說了,遠程握手為敬吧,哈哈)

參考資料

ABP入門教程(一)啟動模版項目

ABP (.Net Core) 使用MySQL數據庫

按照參考資料的操作,只需要簡單幾步就可以把模板項目啟動起來,但是!不知道為何,任何新鮮的技術到了我這裏,就要費上N個步驟,花費九牛二虎之力,絞盡腦汁都還是一堆紅色報錯,滿屏紅讓我沮喪,讓我懷疑人生……

emmmm直接開始從步驟說起吧。

  1. abp模板下載

    abp模板下載

  2. VS2019打開項目解決方案(用VS2017應該也可以,不過要額外下載.net sdk 3.0安裝。)

  3. 找到【解決方案名.Web.Host】項目裏面的appsettings.json,修改數據庫連接字符串
    因為我本地的sqlserver沒有連接成功,缺配置工具,沒有選擇重裝。改換mysql配置。所以我的配置是

    "ConnectionStrings": { "Default": "server=localhost;uid=root;pwd=123;port=3306;database=ExtraModuleDb;" }

  4. 在【解決方案名.EntityFrameworkCore】用Nuget添加安裝引用(也可以參考上面的第二個鏈接,用命令執行)

    install-package pomelo.entityframeworkcore.mysql.design

    install-package pomelo.entityframeworkcore.mysql

  5. 在【解決方案名.EntityFrameworkCore】裏面找到DbContextConfigurer文件,修改數據庫配置連接

     `public static void Configure(DbContextOptionsBuilder<KYKWMSExtraModuleDbContext> builder, string connectionString)
     {
         builder.UseMySql(connectionString);
     }
    
     public static void Configure(DbContextOptionsBuilder<KYKWMSExtraModuleDbContext> builder, DbConnection connection)
     {
         builder.UseMySql(connection);
     }`
    
  6. 數據遷移

    最好先刪除【解決方案名.EntityFrameworkCore】下的【Migration】文件夾

    然後將【解決方案名.Web.Host】設為啟動項目,在程序包管理器控制台(Nuget控制台)里設定默認項目為EntityFrameworkCore。

    在程序包管理器控制台輸入命令: Add-Migration Init

    生成成功,會看到【解決方案名.EntityFrameworkCore】下的【Migration】文件夾下面多了三個文件。

    然後更新數據庫,abp會自動建庫。在程序包管理器控制台輸入命令: Update-Database -Verbose

    到了這裏呢,問題就來了,我正是被這個問題困擾了一個上午。

    好熟悉的錯誤:You have an error in your SQL syntax

    肉眼可見,datetime那裡帶了一個長度,而在mysql語句創建表的時候,datetime類型是不允許帶長度的。

    於是我的做法就是,手動把代碼裏面datetime(6)全部替換成datetime,再次在程序包管理器控制台輸入命令: Update-Database -Verbose

    但得到的結果仍然是You have an error in your SQL syntax。反覆操作 Add-Migration Init Update-Database -Verbose 后,依然沒辦法解決這個問題,我有一點無奈,同時有一點不甘心。我可是個老鳥啊,遇到未知的問題,解決問題的方式就只能是坐着嘆氣了么?! (肯定不是!)

    午休睡了一覺,下午起來接着解決這個問題。我看了一下,為什麼是添加 pomelo.entityframeworkcore.mysql 引用呢?這個產商不是官方Oracle的呢。以前的項目不都是添加MySql.Data嗎?帶着這個疑惑,於是我就嘗試着換 MySql.Data 去添加引用,添加引用的時候,發現它有一個EntityFrameworkCore的版本: MySql.Data.EntityFrameworkCore ,於是把兩個都安裝上。(一定是要兩個都安裝,只安裝 MySql.Data 是不完整的,至少在數據庫配置鏈接出錯的時候,是沒有提示,不知道應該改成UseMySQL才可用)

安裝好后,運行發現有報錯,原來數據庫配置連接裏面的MySql要大寫的才對。(其實報錯的時候會很疑惑,明明是MySql啊,為什麼不支持了呢?也不知道是要改大寫,把鼠標放上去選擇修復的時候,就變成大寫,錯誤就消失了。)

就這樣編譯成功!說明用官方的包也是可以用的!

接着重新重複第6步的命令:

刪除【Migration】文件夾下面的文件,在程序包控制台執行命令:

Add-Migration Init

Update-Database -Verbose

好了!看到表成功的在創建!

但是,不能開心太早!問題又來了!

字符串的長度太長了,最後一句提示varchar max length=65535。我還不知道這些代碼是怎麼生成的,要怎麼把最基礎的代碼改掉才不至於報低級的錯誤。所以在這裏我解決的方法就是手動去把【Migration】文件夾下面的文件,超出數據類型長度的全部手動修改。第一次沒有改全的,報一次錯改一次,後面基本都是數據類型長度超出的問題。全部改好了,再 Update-Database,最終數據庫成功創建好,項目也運行起來了。

emmmm,前端的因為還不太熟悉這種分離式的項目,vue.js還跑不起來,看不到整個項目的全貌。這裏就作為第一部分結束先。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

springboot mybatis plus多數據源輕鬆搞定 (上)

在開發中經常會遇到一個程序需要調用多個數據庫的情況,總得來說分為下面的幾種情況:

  1. 一個程序會調用不同結構的兩個數據庫。
  2. 讀寫分離,兩個數據結構可能一樣高,但是不同的操作針對不同的數據庫。
  3. 混合情況,既有不同的結構的數據庫,也可能存在讀寫分離的情況。

下面針對第一種情況,提供一個解決方案。

解決思路

因為兩個數據庫的功能和結構不一樣,所以可以根據功能和結構把DAO分為兩個package。然後再mapperscan中指定不同的package對接不同的數據源,即可達到多個數據源的共存。

配置yml中的數據源設置

spring:
  datasource:
    emanage:
      url: jdbc:mysql://127.0.0.1:3306/emanage?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&useAffectedRows=true
      username: root
      password: ******
      driver-class-name: com.mysql.cj.jdbc.Driver
    ehr:
      url: jdbc:mysql://127.0.0.1:3306/ehr?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&useAffectedRows=true
      username: root
      password: ********
      driver-class-name: com.mysql.cj.jdbc.Driver

為了不必要的干擾,我把druid數據源的配置部分給刪除了。

建立兩個datasource的配置

datasource1的 配置

@Configuration
@MapperScan(basePackages = {"com.emanage.ehr.mapper.emanage"},sqlSessionTemplateRef = "sqlTemplate1")
public class DataSourceConfig1 {
    @Bean(name = "datasource1")
    @ConfigurationProperties(prefix = "spring.datasource.emanage")
    public DruidDataSource druidDataSource1()
    {
        return DruidDataSourceBuilder.create().build();
    }


    @Bean(name = "sqlFactory1")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("datasource1") DruidDataSource dataSource)
            throws Exception
    {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/emanage/**Mapper.xml"));
        return factoryBean.getObject();
    }

    @Bean(name = "sqlTemplate1")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlFactory1") SqlSessionFactory sqlSessionFactory)
    {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

datasource2的配置

@Configuration
@MapperScan(basePackages = {"com.emanage.ehr.mapper.ehr"},sqlSessionTemplateRef = "sqlTemplate2")
public class DataSourceConfig2 {
    @Bean(name = "datasource2")
    @ConfigurationProperties(prefix = "spring.datasource.ehr")
    public DataSource druidDataSource1()
    {
        return DataSourceBuilder.create().build();
    }


    @Bean(name = "sqlFactory2")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("datasource2") DataSource dataSource)
            throws Exception
    {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/ehr/**Mapper.xml"));
        return factoryBean.getObject();
    }
    @Bean(name = "sqlTemplate2")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlFactory2") SqlSessionFactory sqlSessionFactory)
    {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

兩個datasource的配置基本上一樣。就是建立datasource,sqlsessionFactory,sqlSessionTemplate的注入。然後通過mapperscan來指定具體什麼包採用什麼數據源。然後再對應包里就和以前單數據源一樣操作即可。

注意事項:

如果用myBatis, SqlSessionFactory 部分可以使用SqlSessionFactoryBean來生成。但是如果用mybatis plus一定要用MybatisSqlSessionFactoryBean 來生成SqlSessionFactory。否則會報錯 ,無法直接通過BaseMapper去調用查詢。
如果要再不同的包中混合上XML進行調用。需要在SqlSessionFactory的配置中設置factoryBean.setMapperLocations(resolver.getResources(“classpath*:mapper/ehr/**Mapper.xml”));

優缺點:

優點:
簡單,通過簡單的設置。就可以滿足大多數的情況。

  • 缺點 *:只適合多個數據源的結構完全不一樣,通過package可以分來的方式來調用,不能靈活的在一個package下面隨心所欲的調用數據源。

那麼問題來了:如果要在一個package下面,想調用哪個就調用哪個數據源怎麼辦呢?有時間了,下一篇文章寫寫另外的實現方式。

希望對初學者有價值,如果有疑問歡迎留言交流。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

使用Kubeflow構建機器學習流水線

在此前的文章中,我已經向你介紹了Kubeflow,這是一個為團隊設置的機器學習平台,需要構建機器學習流水線。

在本文中,我們將了解如何採用現有的機器學習詳細並將其變成Kubeflow的機器學習流水線,進而可以部署在Kubernetes上。在進行本次練習的時候,請考慮你該如何將現有的機器學習項目轉換到Kubeflow上。

我將使用Fashion MNIST作為例子,因為在本次練習中模型的複雜性並不是我們需要解決的主要目標。對於這一簡單的例子,我將流水線分為3個階段:

  • Git clone代碼庫

  • 下載並重新處理訓練和測試數據

  • 訓練評估

當然,你可以根據自己的用例將流水線以任意形式拆分,並且可以隨意擴展流水線。

獲取代碼

你可以從Github上獲取代碼:

% git clone https://github.com/benjamintanweihao/kubeflow-mnist.git

以下是我們用來創建流水線的完整清單。實際上,你的代碼很可能跨多個庫和文件。在我們的例子中,我們將代碼分為兩個腳本,preprocessing.py和train.py。

from tensorflow import keras
import argparse
import os
import pickle


def preprocess(data_dir: str):
    fashion_mnist = keras.datasets.fashion_mnist
    (train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

    train_images = train_images / 255.0
    test_images = test_images / 255.0

    os.makedirs(data_dir, exist_ok=True)

    with open(os.path.join(data_dir, 'train_images.pickle'), 'wb') as f:
  pickle.dump(train_images, f)

    with open(os.path.join(data_dir, 'train_labels.pickle'), 'wb') as f:
  pickle.dump(train_labels, f)

    with open(os.path.join(data_dir, 'test_images.pickle'), 'wb') as f:
        pickle.dump(test_images, f)

    with open(os.path.join(data_dir, 'test_labels.pickle'), 'wb') as f:
        pickle.dump(test_labels, f)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Kubeflow MNIST training script')
    parser.add_argument('--data_dir', help='path to images and labels.')
    args = parser.parse_args()

    preprocess(data_dir=args.data_dir)

處理腳本採用單個參數data_dir。它下載並預處理數據,並將pickled版本保存在data_dir中。在生產代碼中,這可能是TFRecords的存儲目錄。

train.py

import calendar
import os
import time

import tensorflow as tf
import pickle
import argparse

from tensorflow import keras
from constants import PROJECT_ROOT


def train(data_dir: str):
    # Training
    model = keras.Sequential([
          keras.layers.Flatten(input_shape=(28, 28)),
          keras.layers.Dense(128, activation='relu'),
          keras.layers.Dense(10)])

    model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                  metrics=['accuracy'])

    with open(os.path.join(data_dir, 'train_images.pickle'), 'rb') as f:
        train_images = pickle.load(f)

    with open(os.path.join(data_dir, 'train_labels.pickle'), 'rb') as f:
        train_labels = pickle.load(f)

    model.fit(train_images, train_labels, epochs=10)

    with open(os.path.join(data_dir, 'test_images.pickle'), 'rb') as f:
        test_images = pickle.load(f)

    with open(os.path.join(data_dir, 'test_labels.pickle'), 'rb') as f:
        test_labels = pickle.load(f)

    # Evaluation
    test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)

    print(f'Test Loss: {test_loss}')
    print(f'Test Acc: {test_acc}')

    # Save model
    ts = calendar.timegm(time.gmtime())
    model_path = os.path.join(PROJECT_ROOT, f'mnist-{ts}.h5')
    tf.saved_model.save(model, model_path)

    with open(os.path.join(PROJECT_ROOT, 'output.txt'), 'w') as f:
        f.write(model_path)
        print(f'Model written to: {model_path}')


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Kubeflow FMNIST training script')
    parser.add_argument('--data_dir', help='path to images and labels.')
    args = parser.parse_args()

    train(data_dir=args.data_dir)

在train.py中,將建立模型,並使用data_dir指定訓練和測試數據的位置。模型訓練完畢並且開始執行評估后,將模型寫入帶有時間戳的路徑。請注意,該路徑也已寫入output.txt。稍後將對此進行引用。

開發Kubeflow流水線

為了開始創建Kubeflow流水線,我們需要拉取一些依賴項。我準備了一個environment.yml,其中包括了kfp 0.5.0、tensorflow以及其他所需的依賴項。

你需要安裝Conda,然後執行以下步驟:

% conda env create -f environment.yml
% source activate kubeflow-mnist
% python preprocessing.py --data_dir=/path/to/data
% python train.py --data_dir=/path/to/data

現在我們來回顧一下我們流水線中的幾個步驟:

  • Git clone代碼庫

  • 下載並預處理訓練和測試數據

  • 訓練並進行評估

在我們開始寫代碼之前,需要從宏觀上了解Kubeflow流水線。

流水線由連接組件構成。一個組件的輸出成為另一個組件的輸入,每個組件實際上都在容器中執行(在本例中為Docker)。將發生的情況是,我們會執行一個我們稍後將要指定的Docker鏡像,它包含了我們運行preprocessing.py和train.py所需的一切。當然,這兩個階段會有它們的組件。

我們還需要額外的一個鏡像以git clone項目。我們需要將項目bake到Docker鏡像,但在實際項目中,這可能會導致Docker鏡像的大小膨脹。

說到Docker鏡像,我們應該先創建一個。

Step0:創建一個Docker鏡像

如果你只是想進行測試,那麼這個步驟不是必須的,因為我已經在Docker Hub上準備了一個鏡像。這是Dockerfile的全貌:

FROM tensorflow/tensorflow:1.14.0-gpu-py3
LABEL MAINTAINER "Benjamin Tan <benjamintanweihao@gmail.com>"
SHELL ["/bin/bash", "-c"]

# Set the locale
RUN echo 'Acquire {http::Pipeline-Depth "0";};' >> /etc/apt/apt.conf
RUN DEBIAN_FRONTEND="noninteractive"
RUN apt-get update  && apt-get -y install --no-install-recommends locales && locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

RUN apt-get install -y --no-install-recommends \
    wget \
    git \
    python3-pip \
    openssh-client \
    python3-setuptools \
    google-perftools && \
    rm -rf /var/lib/apt/lists/*

# install conda
WORKDIR /tmp
RUN wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-4.7.12-Linux-x86_64.sh -O ~/miniconda.sh && \
    /bin/bash ~/miniconda.sh -b -p /opt/conda && \
    rm ~/miniconda.sh && \
    ln -s /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh && \
    echo ". /opt/conda/etc/profile.d/conda.sh" >> ~/.bashrc

# build conda environments
COPY environment.yml /tmp/kubeflow-mnist/conda/
RUN /opt/conda/bin/conda update -n base -c defaults conda
RUN /opt/conda/bin/conda env create -f /tmp/kubeflow-mnist/conda/environment.yml
RUN /opt/conda/bin/conda clean -afy

# Cleanup
RUN rm -rf /workspace/{nvidia,docker}-examples && rm -rf /usr/local/nvidia-examples && \
    rm /tmp/kubeflow-mnist/conda/environment.yml

# switch to the conda environment
RUN echo "conda activate kubeflow-mnist" >> ~/.bashrc
ENV PATH /opt/conda/envs/kubeflow-mnist/bin:$PATH
RUN /opt/conda/bin/activate kubeflow-mnist

# make /bin/sh symlink to bash instead of dash:
RUN echo "dash dash/sh boolean false" | debconf-set-selections && \
    DEBIAN_FRONTEND=noninteractive dpkg-reconfigure dash

# Set the new Allocator
ENV LD_PRELOAD /usr/lib/x86_64-linux-gnu/libtcmalloc.so.

關於Dockerfile值得關注的重要一點是Conda環境是否設置完成並準備就緒。要構建鏡像:

% docker build -t your-user-name/kubeflow-mnist . -f Dockerfile
% docker push your-user-name/kubeflow-mnist

那麼,現在讓我們來創建第一個組件!

在pipeline.py中可以找到以下代碼片段。

Step1:Git Clone

在這一步中,我們將從遠程的Git代碼庫中執行一個git clone。特別是,我想要向你展示如何從私有倉庫中進行git clone,因為這是大多數企業的項目所在的位置。當然,這也是一個很好的機會來演示Rancher中一個很棒的功能,它能簡單地添加諸如SSH密鑰之類的密鑰。

使用Rancher添加密鑰

訪問Rancher界面。在左上角,選擇local,然後選擇二級菜單的Default:

然後,選擇Resources下的Secrets

你應該看到一個密鑰的列表,它們正在被你剛剛選擇的集群所使用。點擊Add Secret:

使用你在下圖中所看到的值來填寫該頁面。如果kubeflow沒有在命名空間欄下展示出來,你可以通過選擇Add to a new namespace並且輸入kubeflow簡單地創建一個。

確保Scope僅是個命名空間。如果將Scope設置為所有命名空間,那麼將使得在Default項目中的任意工作負載都能夠使用你的ssh密鑰。

在Secret Values中,key是id_rsa,值是id_rsa的內容。完成之後,點擊Save。

如果一些進展順利,你將會看到下圖的內容。現在你已經成功地在kubeflow命名空間中添加了你的SSH密鑰,並且無需使用kubectl!

既然我們已經添加了我們的SSH key,那麼是時候回到代碼。我們如何利用新添加的SSH密鑰來訪問私有git倉庫?

def git_clone_darkrai_op(repo_url: str):

    volume_op = dsl.VolumeOp(
        name="create pipeline volume",
        resource_name="pipeline-pvc",
        modes=["ReadWriteOnce"],
        size="3Gi"
    )

    image = 'alpine/git:latest'

    commands = [
        "mkdir ~/.ssh",
        "cp /etc/ssh-key/id_rsa ~/.ssh/id_rsa",
        "chmod 600 ~/.ssh/id_rsa",
        "ssh-keyscan bitbucket.org >> ~/.ssh/known_hosts",
        f"git clone {repo_url} {PROJECT_ROOT}",
        f"cd {PROJECT_ROOT}"]

    op = dsl.ContainerOp(
        name='git clone',
        image=image,
        command=['sh'],
        arguments=['-c', ' && '.join(commands)],
        container_kwargs={'image_pull_policy': 'IfNotPresent'},
        pvolumes={"/workspace": volume_op.volume}
    )

    # Mount Git Secrets
    op.add_volume(V1Volume(name='ssh-key-volume',
                           secret=V1SecretVolumeSource(secret_name='ssh-key-secret')))
    op.add_volume_mount(V1VolumeMount(mount_path='/etc/ssh-key', name='ssh-key-volume', read_only=True))

    return op

首先,創建一個Kubernetes volume,預定義大小為3Gi。其次,將image變量指定為我們將要使用的alpine/git Docker鏡像。之後是在Docker容器中執行的命令列表。這些命令實質上是設置SSH密鑰的,以便於流水線可以從私有倉庫git clone,或者使用git://URL來代替 https://。

該函數的核心是下面一行,返回一個dsl.ContainerOp。

command和arguments指定了執行鏡像之後需要執行的命令。

最後一個變量十分有趣,是pvolumes,它是Pipeline Volumes簡稱。它創建一個Kubernetes volume並允許流水線組件來共享單個存儲。該volume被掛載在/workspace上。那麼這個組件要做的就是把倉庫git clone到/workspace中。

使用Secrets

再次查看命令和複製SSH密鑰的位置。

流水線volume在哪裡創建呢?當我們將所有組件都整合到一個流水線中時,就會看到創建好的volume。我們在/etc/ssh-key/上安裝secrets:

op.add_volume_mount(V1VolumeMount(mount_path='/etc/ssh-key', name='ssh-key-volume', read_only=True))

請記得我們將secret命名為ssh-key-secret:

op.add_volume(V1Volume(name='ssh-key-volume',
                           secret=V1SecretVolumeSource(secret_name='ssh-key-secret')))

通過使用相同的volume名稱ssh-key-volume,我們可以把一切綁定在一起。

Step2:預處理

def preprocess_op(image: str, pvolume: PipelineVolume, data_dir: str):
    return dsl.ContainerOp(
        name='preprocessing',
        image=image,
        command=[CONDA_PYTHON_CMD, f"{PROJECT_ROOT}/preprocessing.py"],
        arguments=["--data_dir", data_dir],
        container_kwargs={'image_pull_policy': 'IfNotPresent'},
        pvolumes={"/workspace": pvolume}
    )

正如你所看到的, 預處理步驟看起來十分相似。

image指向我們在Step0中創建的Docker鏡像。

這裏的command使用指定的conda python簡單地執行了preprocessing.py腳本。變量data_dir被用於執行preprocessing.py腳本。

在這一步驟中pvolume將在/workspace里有倉庫,這意味着我們所有的腳本在這一階段都是可用的。並且在這一步中預處理數據會存儲在/workspace下的data_dir中。

Step3:訓練和評估

def train_and_eval_op(image: str, pvolume: PipelineVolume, data_dir: str, ):
    return dsl.ContainerOp(
        name='training and evaluation',
        image=image,
        command=[CONDA_PYTHON_CMD, f"{PROJECT_ROOT}/train.py"],
        arguments=["--data_dir", data_dir],
        file_outputs={'output': f'{PROJECT_ROOT}/output.txt'},
        container_kwargs={'image_pull_policy': 'IfNotPresent'},
        pvolumes={"/workspace": pvolume}
    )

最後,是時候進行訓練和評估這一步驟。這一步唯一的區別在於file_outputs變量。如果我們再次查看train.py,則有以下代碼段:

    with open(os.path.join(PROJECT_ROOT, 'output.txt'), 'w') as f:
        f.write(model_path)
        print(f'Model written to: {model_path}')

我們正在將模型路徑寫入名為output.txt的文本文件中。通常,可以將其發送到下一個流水線組件,在這種情況下,該參數將包含模型的路徑。

將一切放在一起

要指定流水線,你需要使用dsl.pipeline來註釋流水線功能:

@dsl.pipeline(
    name='Fashion MNIST Training Pipeline',
    description='Fashion MNIST Training Pipeline to be executed on KubeFlow.'
)
def training_pipeline(image: str = 'benjamintanweihao/kubeflow-mnist',
                      repo_url: str = 'https://github.com/benjamintanweihao/kubeflow-mnist.git',
                      data_dir: str = '/workspace'):
    git_clone = git_clone_darkrai_op(repo_url=repo_url)

    preprocess_data = preprocess_op(image=image,
                                    pvolume=git_clone.pvolume,
                                    data_dir=data_dir)

    _training_and_eval = train_and_eval_op(image=image,
                                           pvolume=preprocess_data.pvolume,
                                           data_dir=data_dir)

if __name__ == '__main__':
    import kfp.compiler as compiler
    compiler.Compiler().compile(training_pipeline, __file__ + '.tar.gz')

還記得流水線組件的輸出是另一個組件的輸入嗎?在這裏,git clone、container_op的pvolume將傳遞到preprocess_cp。

最後一部分將pipeline.py轉換為可執行腳本。最後一步是編譯流水線:

% dsl-compile --py pipeline.py --output pipeline.tar.gz

上傳並執行流水線

現在要進行最有趣的部分啦!第一步,上傳流水線。點擊Upload a pipeline:

接下來,填寫Pipeline Name和Pipeline Description,然後選擇Choose file並且指向pipeline.tar.gz以上傳流水線。

下一頁將會展示完整的流水線。我們所看到的是一個流水線的有向無環圖,在本例中這意味着依賴項會通往一個方向並且它不包含循環。點擊藍色按鈕Create run 以開始訓練。

大部分字段已經已經填寫完畢。請注意,Run parameters與使用@ dsl.pipeline註釋的training_pipeline函數中指定的參數相同:

最後,當你點擊藍色的Start按鈕時,整個流水線就開始運轉了!你點擊每個組件並查看日誌就能夠知道發生了什麼。當整個流水線執行完畢時,在所有組件的右方會有一個綠色的確認標誌,如下所示:

結論

如果你從上一篇文章開始就一直在關注,那麼你應該已經安裝了Kubeflow,並且應該能體會到大規模管理機器學習項目的複雜性。

在這篇文章中,我們先介紹了為Kubeflow準備一個機器學習項目的過程,然後是構建一個Kubeflow流水線,最後是使用Kubeflow接口上傳並執行流水線。這種方法的奇妙之處在於,你的機器學習項目可以是簡單的,也可以是複雜的,只要你願意,你就可以使用相同的技術。

因為Kubeflow使用Docker容器作為組件,你可以自由地加入任何你喜歡的工具。而且由於Kubeflow運行在Kubernetes上,你可以讓Kubernetes處理機器學習工作負載的調度。

我們還了解了一個我喜歡的Rancher功能,它十分方便,可以輕鬆添加secrets。立刻,你就可以輕鬆地組織secrets(如SSH密鑰),並選擇將其分配到哪個命名空間,而無需為Base64編碼而煩惱。就像Rancher的應用商店一樣,這些便利性使Kubernetes的工作更加愉快,更不容易出錯。

當然,Rancher提供的服務遠不止這些,我鼓勵你自己去做一些探索。我相信你會偶然發現一些讓你大吃一驚的功能。Rancher作為一個開源的企業級Kubernetes管理平台,Run Kubernetes Everywhere一直是我們的願景和宗旨。開源和無廠商鎖定的特性,可以讓用戶輕鬆地在不同的基礎設施部署和使用Rancher。此外,Rancher極簡的操作體驗也可以讓用戶在不同的場景中利用Rancher提升效率,幫助開發人員專註於創新,而無需在繁瑣的小事中浪費精力。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※FB行銷專家,教你從零開始的技巧

淺析pplx庫的設計與實現。

主要有三部分組成,threadpool,scheduler,task。

 

 三者關係如上圖示,pplx只着重實現了task部分功能,scheduler跟threadpool只是簡略實現。

 threadpool主要依賴boost.asio達到跨平台的目標,cpprestsdk的 io操作同時也依賴這個threadpool。

pplx提供了兩個版本的scheduler,分別是

linux_scheduler依賴boost.asio.threadpool。

window_schedule依賴win32 ThreadPool。

默認的scheduler只是簡單地將work投遞到threadpool進行分派。

用戶可以根據自己需要,實現scheduler_interface,提供複雜的調度。

 

每個task關聯着一個_Task_impl實現體,一個_TaskCollection_t(喚醒事件,後繼任務隊列,這個隊列的任務之間的關係是並列的),還有一個_PPLTaskHandle代碼執行單元。

task,并行執行的單位任務。通過scheduler將代碼執行單元調度到線程去執行。

task提供類似activeobject模式的功能,可以看作是一個future,通過get()同步阻塞等待執行結果。

task提供拓撲模型,通過then()創建後續task,並作為後繼執行任務。注意的是每個task可以接受不限數量的then(),這些後繼任務之間並不串行。例 task().then().then()串行,(task1.then(), task1.then())并行。一個任務在執行完成時,會將結果傳遞給它的所有直接後繼執行任務。

 

此外,task拓撲除了then()函數外,還可以在執行lambda中添加并行分支,然後可以在後繼任務中同步這些分支。

也就是說後繼任務同步原本task拓撲外的task拓撲才能繼續執行。

 1 auto fork0 =
 2      task([]()->task<void>{
 3         auto fork1 = 
 4  task([]()->task<void>{ 5 auto fork2 = 6  task([](){ 7 // do your fork2 work 8 9  }); 10 // do your fork1 work 11 12 return fork2; 13 }).then([](task<void>& frk2){ frk2.wait(); }); // will sync fork2 14 // do your fork0 work 15 16 return fork1; 17 }).then([](task<void>& frk1){ frk1.wait(); }); // will sync fork1 18 fork0.wait(); // sync fork1, fork2

上面的方式有一個問題,如果裡層的fork先完成,將不要阻塞線程,但是外層fork先完成就不得不阻塞線程等待內層fork完成。

所以可以用when_all

task<task<void> >([]()->task<void> {
    std::vector<task<void> > forks; forks.push_back( task([]() { /* do fork0 work */ }) ); forks.push_back( task([]() { /* do fork1 work */ }) ); forks.push_back( task([]() { /* do fork2 work */ }) ); forks.push_back( task([]() { /* do fork3 work */ }) ); return when_all(std::begin(forks), std::end(forks)); }).then([](task<void> forks){ forks.wait(); }).wait();

通過上面的方式,也可以在lambda中,將其它task拓撲插入到你原來的task拓撲。

在include/cpprest/atreambuf.h實現的_do_while就是這樣一個例子

template<class F, class T = bool>
pplx::task<T> _do_while(F func)
{
    pplx::task<T> first = func();
    return first.then([=](bool guard) -> pplx::task<T> {
        if (guard)
            return pplx::details::_do_while<F, T>(func);
        else
            return first;
    });
}

如果func不返回一個false,就會無限地在first.then()這兩任務拓撲結束前,再插入多一個first.then()任務拓撲,無限地順序地執行下去,如loop一樣地進行。

  

task結束,分兩種情況,完成以及取消。取消執行,只能在執行代碼時通過拋出異常,task並沒有提供取消的接口。任務在執行過程中拋出的異常,就會被task捕捉,並暫存異常,然後取消執行。異常在wait()時重新拋出。下面的時序分析可以看到全過程 。

 

 

值得注意的是,PPL中task原本的設計是的有Async與Inline之分的。在_Task_impl_base::_Wait()有一小段註釋說明

// If this task was created from a Windows Runtime async operation, do not attempt to inline it. The
// async operation will take place on a thread in the appropriate apartment Simply wait for the completed
// event to be set.
            
                

也就是task除了由scheduler調度到線程池分派執行,還可以強制在wait()函數內分派執行,後繼task也不必再次調度而可以在當前線程繼續分派執行。但是pplx沒有實現

class _TaskCollectionImpl
{
    ...
    void _Cancel() { // No cancellation support  } void _RunAndWait() { // No inlining support yet  _Wait(); }

 

現在再來比較 task<_ReturnType> 與 task< task<_ReturnType > >,當一個前驅任務拋出異常中止后,如果前驅任務是task<_ReturnType>的話,後續任務的lambda參數就是_ReturnType,由後續任務執行_Continue時代為執行了前驅任務的get(),這時就會rethrow異常,然後就直接中止後續任務。但是如果後續任務的lambda參數是task<_ReturnType>的話,用戶的lambda就有機會處理前驅任務的錯誤異常。所以就有了 task_from_result<_ReturnType>跟task_from_exception兩個函數,將結果或異常轉化成task,以符合後續任務的lambda的參數要求。

 

 下面是對task的時序分析。

開始的task創建_InitialTaskHandle, 一種只能用於始首的Handle執行單元。

 

通過then()添加的task,創建_ContinuationTaskHandle,(一種可以入鏈的後繼執行單元),並暫存起來。

 

當一個任務在線程池中分派結束時,就會將所有通過then()添加到它結尾的後繼任務一次過向scheduler調度出去。

任務只能通過拋出異常從而自己中止執行,task並暫存異常(及錯誤信息)。

 

 

 後繼任務被調度到線程池繼續分派執行。

 

這裏順便討論一個開銷,在window版本中,每個task都有一個喚醒事件,使用事件內核對象,都要創建釋放一個內核對象,在高并行任務時,可能會消耗過多內核對象,消耗句柄數。

並且continuation後繼任務,在默認scheduler調度下,不會在同一線程中分派,所有後繼任務都會簡單投遞到線程池。由線程池去決定分派的線程。所以由then()串行起來的任務可能會由不同的線程順序分派,從而產生開銷。因為pplx並沒有實現 Inline功能,所有task都會視作Async重新調度到線程池。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

※網頁設計最專業,超強功能平台可客製化

Git 居然可以用來跟女神聊天?

Git 是用來做啥的?想必碼農朋友都知道,Git 是版本控制軟件,是軟件開發過程中團隊協作不可或缺的軟件。

但是,作為版本控制軟件的 Git ,能跟聊天工具扯上關係嗎?這二者似乎毫無關係,但腦洞大開的外國朋友 Ephi Gabay 就開發了一個 GIC ,活生生將 Git 改造成了一個聊天工具,有了它你就可以跟女神親密溝通了!

這位哥們是用 Node.js 寫了這麼一個工具,將 Git 改裝成後台數據庫!更詳細的,他其實是把每一句聊天作為 commit 的內容提交到倉庫里!所以,執行 git log 時,可以看到完整的對話過程。想必當年 Linus 怎麼也不會想到,他寫的 Git 會這麼被人改造!

這個項目的地址如下:

https://github.com/ephigabay/GIC

下面良許帶你一步步實現這個騷過程。

1. 創建一個倉庫

因為整個聊天的過程,其實就是不停在提交的過程,所以我們需要創建一個倉庫。這個倉庫,肯定不能是你現在工作用的倉庫,否則你之前的工作過程就全玩完了。

倉庫的創建不難吧?這裏簡單演示一下:

mkdir gitchat
cd gitchat
git init
echo "chat logs" > README
git add README
git commit -m 'fist commit'

2. 安裝 GIC

既然 GIC 是基於 Git 的,那麼 Git 肯定是需要安裝的。而且 GIC 是使用 Node.js 編寫的,所以需要安裝 nodejs 。後面我們還需要用到 npm 命令,所以我們還需要安裝一個 npm 。

如果是 Ubuntu 平台的話,安裝過程可以使用以下命令:

sudo apt-get install git nodejs npm

如果是其它平台,請參照各自平台的安裝指導手冊。

然後,我們需要將 GIC 這個項目拷到自己的電腦上,如下:

git clone https://github.com/ephigabay/GIC GIC

等 GIC 完整拷備到電腦上后,我們進入到目錄里並安裝一些依賴文件:

cd GIC
npm install

這個安裝過程可能要花費一些時間,靜靜等待即可。

3. 配置 GIC

對於 GIC 我們只需要配置第 1 步所建的那個倉庫路徑即可,需要編輯 config.js 文件的 gitRepo 字段:

module.exports = {
    gitRepo: '/home/pi/tests/gitchat/.git', #配置你的聊天倉庫路徑
    messageCheckInterval: 500,
    branchesCheckInterval: 5000
};

在正式開始聊天之前,我們先試一下配置是否正確:

git clone --quiet /home/pi/tests/gitchat/.git > /dev/null

如果上面那步沒報錯的話,說明你所配置的路徑就是正確的。

接下來,我們就可以正式開始和女神聊天了。

4. 使用 Git 開始聊天

開始聊天時,我們可以在 GIC 目錄里使用以下命令啟動聊天:

npm start

之後,你就會看到一個文字版的聊天窗口了。左邊就是聊天內容,右邊是分支。不同的分支就是不同的通道,相當於不同的聊天室,裏面的聊天內容也是不同的。

但是,請注意,如果倉庫里你當前所在的分支是 master 分支,那麼你就不能在這個分支里聊天,要切到其它分支聊天,否則會報錯。

如果要多人聊天的話,每個用戶只需進到 GIC 目錄,然後執行 npm start 命令即可參与聊天。

前面說了,這個聊天的過程其實是依託 git log ,所以我們在 git log 里可以看到完整的聊天記錄:

pi@raspberrypi:~/tests/gitchat $ git log --pretty=format:"%p %cn %s" dev
371a477 evis hao a, wanshange jiu qu!
b6cc4ae alvin yan wo ye hen hao, yao bu yao qu gongyuan zouzou?
7bfea8f evis fine, good, and you?
017d82f alvin yan hello evis, how are you?
 alvin yan init commit

5. 退出聊天

有兩種方法:

  1. 按一下 Esc ,再按 ctrl+C
  2. 執行以下命令:
sudo kill `pgrep npm`

公眾號:良許Linux

有收穫?希望老鐵們來個三連擊,給更多的人看到這篇文章

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

Honda和GM發展氫燃料電池,最快2020生產

在鋰電池電動車發展趨穩定的現階段,也有另一派人支持發展氫動力燃料電動車。美國本田汽車(Honda)和通用汽車(GM)宣布將攜手發展氫動力燃料電池新系統,預計2020 年投產。

美國加州政策規定,自2018年起,各家汽車製造商要銷售一定比例之「零排放車」。看準美國對電動車需求將增加,兩公司於1月30日宣布,將共同出資成立公司,合作發展氫動力電池系統,廠址將設於通用美國密西根州的工廠內。而本田汽車目前於日本櫪木縣高根澤町的工廠,之後將逐步停產。

鋰電池電動車和氫燃料電動車各有其擁護者與利弊。雖然兩者都被列為「零汙染」汽車,但實際上鋰電池電動車環保與否還要追溯到發電的來源,而氫動力燃料電池則有甲烷和一氧化碳排放的疑慮。充電方面,電動車充電耗時;氫燃料補充速度和油槍一般,只是日後加氫站的設點還有待規劃,安全性的需求也更高。

本田和通用自2013年就已展開合作。本次協議各出資一半資金成立新公司,目標是降低氫燃料電池成本較高的難題,並在環保車競爭市場保有優勢。

(首圖來源:General Motors)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

受樂視影響,法拉第縮小工廠規模

根據路透社的報導,中國影視廠商樂視網旗下子公司,豪華電動車新創公司法拉第未來(Faraday Future),已經決定縮減在美國興建車廠的規模。組裝廠規模將由原來佔地300 萬平方英尺,大幅縮減至65 萬平方英尺,生產車款數量亦會由7 款大減至2 款。

報導指出,法拉第曾於2015 年表示,總投資金額達到13 億美元,在內華達州北拉斯維加斯的組裝廠年產可高達15 萬輛,而且2017 年底投產。不過根據目前的狀況顯示,法拉第目前該工廠得預定產能每年將少於1 萬輛,而且還必須延後到2019 年才開始正式量產。

由於受到前一段時間母公司樂視網財務困境的影響,法拉第工廠一度傳出高層離職、欠供應商貨款、甚至是停工歇業的消息。雖然,在母公司獲得人民幣168 億元的資金援助後,工廠重新啟動。不過,可能也是因為這樣的原因,使得樂事網不得不宣布縮小其工廠的規模與未來的生產能量。

在2017 年的CES 展上,法拉第推出首輛量產車款FF91 之後,由於具備高度智慧連網功能,以及搭載時下最熱門的自動駕駛技術。並且全車配備3D 雷達、10 個高感度相機、13 個長距離和短距離雷達以及12 個超聲波感測器,可以說性能超越市面上包括賓利、法拉利和特斯拉的所有電動車。但即便如此,分析師仍認為,法拉第FF91 的推出依舊是遠水救不了近火。

有統計指出,未來幾年內,樂視網在法拉第汽車上的資金缺口約在人民幣460 億元以上。而根據樂視網在2017 年1 月13 日發布的公告表示,引入包括融創中國在內的多家戰略投資,投資金額為人民幣168 億元的資金,則將汽車業務排除在外。換言之,樂事網的汽車豪賭,未來還需要再另籌資金。所以,在FF91 售價高達人民幣200 萬元,再加上樂視網資金仍有隱憂的情況下,法拉第工廠能不能順利在2019 年生產、上路,恐怕還有變數。

(合作媒體:。圖片出處:Faraday Future)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

智慧汽車前景看好,車用LED廠展開投資

近來,陸續有汽車與科技大廠於加州展開無人駕駛路上測試,智慧汽車儼然成為科技市場的新藍海。這股趨勢也吸引光學鏡頭和LED廠商展開投資。

看好無人駕駛技術發展潛力,除Tesla、Ford、GM與Goole等廠商展開道路測試外,也有鏡頭廠和LED廠開始投入。自駕車上路仰賴感測器進行路況偵測,因此感測器和車燈比以往更受重視,以滿足嚴格的安全性標準。

在車用LED市場方面,日本Panasonic(松下)有意收購歐洲車燈大廠ZKW,結合自家感測器專長,攻自駕車市場;而據《聯合財經網》報導,台廠群光以子公司群電買下中國車燈廠以進攻其供應鏈,且與LED車燈廠頻繁接觸,同樣有意搶食車用LED這塊大餅。

另外,照明大廠歐司朗(OSRAM)在2017年調降LED車用成本,為的是加快市場滲透率。其採用金屬導線架以取代原先LED陶瓷散熱基板,除成本降低之外,也利於設計;且其車尾燈、方向燈等產品均已改用EMC導線架。

《聯合財經網》指出,有供應商表示,除了LED大燈仍採用陶瓷基板外,其餘車體內外的晝型燈可能改用EMC導線架。隨車用LED應用變多、EMC導線架的使用增加,中國LED晝型的燈滲透率也逼近50%。

 (首圖來源: CC2.0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

必翔取得首款台產電動汽車生產執照

台灣的電動機車品牌Gogoro已經能從台北一路騎到高雄,還將進軍歐洲市場。而台灣首款本地生產的電動汽車也正式領取生產執照,將由在地廠商必翔集團負責產銷。

必翔集團在台成立三十餘年,早期曾投入農業機械研發,近期則以電動代步車、醫療用車等車款代工為主要業務。看好全球電動車市場蓬勃,必翔於2011年正式成立必翔電動汽車公司,並獲得中國廠商比亞迪(BYD)的肯定,合作發展電動汽車技術。

必翔在宜蘭縣建有電動汽車組裝廠,客戶行銷歐洲。日前,必翔已成功取得台灣首張電動汽車生產執照,預計將在今年第三季前量產問世。

除電動汽車公司外,必翔集團旗下另一子公司必翔電能為磷酸鋰鐵電池廠,廠房位於新竹,每月可生產100萬顆18650鋰鐵電池,集團整體可形成電動車產業的垂直整合。為提供日漸提升的電動車用電池需求,新竹廠房將陸續擴產到目前規模的10倍;公司也已申請掛牌上市,正在等待審核。

必翔集團也積極投入再生能源發展。必翔電動汽車的,由台灣永鑫能源負責開發、雲豹能源科技出資,完全採用美商First Solar的太陽能板,是First Solar在亞洲規模最大的屋頂型太陽能發電廠。

(照片:必翔公司廠房。來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

台灣盼衝電動車款,2017電動機車目標4萬輛

台灣有2,300萬人口與1,400萬輛掛牌機車,機車普及率極高。而電動機車在其中只佔6萬輛,還有龐大的市場空間。台灣政府希望提高電動車充電站的設置密度,藉此推動電動車需求成長,目標在今年銷售四萬輛、2021年達到20萬輛。

台灣在1999年開始推動電動機車,但整體市場需求在2015年本土品牌Gogoro加入後才逐漸明朗化。2016年,台灣電動機車的銷售量較2015年增加了一倍,達2萬輛,其中有1.3萬輛是Gogoro車款;其次則是中華汽車的電動機車,2016年銷量4,600輛。在需求類別方面,從自用車到物流、外送等,都有分布。

電動車充電設施若能廣泛鋪設,就能吸引更多駕駛加入電動車款行列,在世界各地都是不變的原則。台灣經濟部長李世光對此表示,未來將繼續推動電動車充電網絡建設,目標六都每1公里就設置一座電池交換站或充電站,同時也將加強偏鄉與觀光區的充電設施建設。

目前,台灣政府補助輕型電動機車每輛新台幣7,200元,重型電動機車每輛10,000元;各地方政府也有金額不等的加碼補助。優惠措施加上充電設施普及,預期將能更有效推動需求。2017年的銷售目標是較2016年再倍增的4萬輛

電動汽車銷量如何?先看Tesla

雖然台灣2016年電動機車的銷量達到2萬輛,但對比機車總銷量80萬輛而言,只佔2.5%,還有很大的發展空間。

而2016年台灣電動車市的另一大消息,非美商TESLA來台莫屬。Tesla電動汽車已經正式在台交車,一月有80輛完成交車手續,均為Model S。其中,又以 Model S 90D車款銷量最好。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※網頁設計最專業,超強功能平台可客製化