發佈日期:

AI現況的概觀

人工智慧是第四次的工業革命

Mobile computing, inexpensive sensors collecting terabytes of data, and the rise of machine learning that can use that data will fundamentally change the way the global economy is organized.

Fortune, “CEOs: The Revolution is Coming,” March 2016

移動計算、廉價感測器收集的大量數據以及機器學習的興起將從根本上改變全球經濟的組織方式

移動計算指的是人們使用移動設備(如智能手機、平板電腦)進行計算和資訊處理的能力。隨著移動設備的普及和技術的發展,人們能夠隨時隨地存取資訊和進行各種計算任務,不再受限於固定的位置或時間。這使得商業活動和經濟交易可以更靈活地進行,並且在不同的地點和時間進行交流和合作。

同時,廉價的感測器技術讓我們能夠收集大量的數據。感測器可以應用於各種物理環境,例如工業設備、城市基礎設施、交通系統、健康監測等。這些感測器可以收集和傳輸各種類型的數據,例如溫度、壓力、位置、運動、光線等。由於感測器成本的下降,人們可以以更低的成本部署大量的感測器網絡,從而收集和分析大量的數據。這些數據提供了對於各種系統和環境的詳細洞察,並且為企業和政府制定更有效的決策提供了依據。

同時,機器學習的快速發展使得我們能夠有效地處理和分析這些大量的數據。機器學習是一種人工智慧的分支,它利用數據和算法來使計算機自動學習並提高性能。通過機器學習,我們能夠從數據中發現模式、趨勢和關聯性,並且能夠預測和做出更準確的決策。這對於企業的營銷、客戶服務、生產優化、預測分析等各個領域都具有重要意義。

AI會造成那些變革呢?

在軟體開發中的應用:AI被應用於軟體開發的各個階段和領域。例如,AI可以用於自動化測試和驗證、代碼生成和自動化編程、缺陷檢測和修復、軟體需求分析和優化等。AI的應用可以提高開發效率、減少錯誤和提供更好的軟體品質。

軟體開發人員可以利用現有的AI工具和平台來開發具有智能功能的軟體應用。AI工具和框架的開放性和可用性不斷提高,使得開發人員能夠更容易地整合AI技術到他們的軟體項目中。

軟體定義硬體(Software-Defined Hardware)指的就是利用軟體控制和編程來定義和配置硬體資源。AI的發展促使軟體定義硬體的興起,因為AI需要大量的計算資源和專用硬體來實現高效的運算。軟體定義硬體技術可以通過軟體來定義和配置硬體資源,以適應不同的AI工作負載和需求。

對於不同的應用場景,可以選擇不同的AI算法和技術。例如,時間序列分析可以使用統計方法、機器學習模型或深度學習模型等不同的技術來處理。對於每個應用領域,了解和理解相關的AI技術,並選擇合適的方法和工具是非常重要的。

AI取代的是職能不是產業

晶片技術的進步和創新為人工智慧的發展提供了強大的計算能力和效能提升。

AI將重新定義產業

  • 重新定義員工的技能: 核心技能的重新訓練提升
  • 重新定義部門的功能: 把智能化當成再進化的機會
  • 重新定義組織的效能: 創造進入的門檻與護城河

Al核心技術

機器學習

  • 監督式學習
  • 非監督式學習
  • 模型參數優化

深度學習

  • 卷積神經網路(CNN)
  • 深度神經網路(DNN)
  • 循環神經網路(RNN)
  • 神經網路與網路參數優化

進階學習技術

  • 強化學習(Reinforcement Learning)是一種機器學習的方法,旨在使智能體(agent)通過與環境的交互行為學習最佳的行動策略,以最大化預期的回報(reward)。
  • 遷移式學習(Transfer Learning)旨在將在一個任務或領域上學習到的知識和經驗應用於另一個相關或相似的任務或領域中。它假設前一個任務的學習可以為後續的學習提供有用的信息和特徵。
  • 對抗式學習
  • 聯邦式學習(Federated Learning)通常涉及在多個參與方的本地數據上進行分散訓練,每個參與方使用本地數據訓練局部模型,然後將模型的更新參數聚合到全局模型中。每個參與方可以具有不同的數據分布和特徵,而原始數據通常不共享。

工具框架

  • Al應用開發工具,如: C、Python、JavaScript
  • Al模型開發框架,如: TensorFlow、PyTorch、Keras(可以運行在TensorFlow、PyTorch等後端上)、Scikit-learn
  • Al部署工具與平台: 與傳統程式開發的CI/CD不同之處在於這邊是對AI模型進行版本管理,以便追蹤和管理不同版本的模型,因為模型的更新和改進是一個持續的過程。相比之下,傳統程式開發中的版本管理更偏向於代碼和軟體功能的版本控制。
  • Al系統整合與開發: 將人工智能(AI)技術和解決方案集成到現有的系統或應用程序中,以實現更智能化、自動化或增強的功能

資料治理

  • 資料管理架構
  • 特徵工程
  • 資料品質

AI應用技術

電腦視覺

自然語言處理

  • 情緒分析
  • 語言翻譯
  • 文本分析與生成
  • 社群媒體與社交網路分析
  • 聊天機器人

數據分析與預測

  • 資料建模與調校
  • 數據分析與預測
  • 時間序列分析與預測

語音處理

  • 語音辨識
  • 語音合成
  • 情感分析
  • 語音索引

推薦系統

  • 關聯式推薦(Association-based Recommendation):基於物品之間的關聯性進行推薦。它假設物品之間的關聯性可以用共現或相似性來衡量。例如,如果用戶購買了商品A,則可以通過關聯分析推薦與商品A相關的商品B。
  • 內容推薦(Content-based Recommendation):根據用戶過去的行為和對物品的偏好,推薦與用戶喜好相似的物品。它使用物品的屬性和特徵來建模和描述物品,並基於這些特徵計算用戶的興趣相似度。常見的方法是使用機器學習算法,如TF-IDF(Term Frequency-Inverse Document Frequency)和餘弦相似度來計算相似度並進行推薦。例如,如果用戶喜歡觀看某部電影,則可以根據該電影的特徵和用戶的偏好,推薦與該電影相似的其他電影。
  • 協同過濾推薦(Collaborative Filtering Recommendation):根據用戶的歷史行為和與其他用戶的相似性來進行推薦。它假設具有相似行為模式的用戶會有相似的偏好,並根據這種相似性來推薦物品。協同過濾可以分為基於用戶(User-based)和基於物品(Item-based)的方法。基於用戶的協同過濾將根據其他用戶對物品的評價來推薦物品給目標用戶,而基於物品的協同過濾則根據物品之間的相似度來推薦。
  • 深廣學習推薦系統(Deep Learning Recommendation Systems):深廣學習推薦系統使用深度神經網絡模型來捕捉用戶和物品之間的複雜關係,以提供個性化的推薦。這些模型可以學習到高維度的嵌入式表示,並進一步進行用戶興趣建模和物品推薦。協同過濾推薦系統需要大量的用戶行為數據(如評分或點擊數據),而內容推薦系統需要豐富的物品特徵數據。深廣學習推薦系統則需要用戶行為和物品特徵的結合數據。

新興應用技術

  • Meta 元宇宙涉及虛擬現實技術、3D 建模、社交媒體和區塊鏈等多種技術,AI 在其中扮演著一個重要的角色,用於實現虛擬世界的智能化、自動化和個性化。
  • Web 3.0中包括去中心化、區塊鏈和加密貨幣等技術的應用。AI 在 Web 3.0 中也扮演著重要的角色,特別是在區塊鏈智能合約、數據分析和自動化等方面的應用。AI 技術可以用於分析和處理大量的數據,提供智能合約的自動執行和驗證機制,並改進用戶的互動和體驗。

Al產業應用

智慧製造

  • 產品檢測/瑕疵檢測
  • 規劃排程/自動化流程
  • 設備維護預測
  • 物流整合/庫存管理
  • 自動化機械

智慧醫療

  • 醫學影像與診斷
  • 醫療訊號分析與偵測
  • 病歷報告分析
  • 健檢與輔助醫療照護
  • 預測及風險分析

智慧零售

  • 影像辨識(顧客、商品等)
  • 智能客服
  • 智慧貨架/庫存管理
  • 倉儲與物流管理
  • 顧客分析/需求預測

智慧金融

  • 證件辨識
  • 生物辨識
  • 詐欺洗錢防制AML
  • 最佳投資組合

邊緣運算

一種在邊緣設備或邊緣網路中執行人工智能(AI)任務的計算方式。它將AI的計算和推理能力移到距離數據生成源頭更接近的地方,以減少延遲、節省頻寬並提高效能。

  • 邊緣運算強調將計算資源和AI能力移近到數據生成源頭的重要性,以應對處理大量數據和低延遲要求的應用。生態系則涉及邊緣運算相關的技術、產品和服務提供商,包括硬體設備、軟體平台、開發工具等。
  • 霧運算(Fog Computing)是指在邊緣設備和雲端之間建立的一層中介計算層。它將計算和存儲資源分佈到邊緣網路中,使數據能夠在邊緣設備和雲端之間進行處理和分析。分散式協作架構則關注在邊緣環境中實現多個設備之間的協作和溝通,以實現更複雜的任務和應用。
  • 主流的邊緣計算方案和工具庫提供了在邊緣設備上執行AI任務的技術和工具。這些方案和工具庫通常包括優化的AI模型、運行時環境、軟體開發工具和部署工具等。它們使開發人員能夠將AI能力整合到邊緣設備中,實現本地化的AI處理。
  • 5G和物聯網是邊緣運算的重要驅動力之一。5G技術提供了更高的帶寬、更低的延遲和更多的連接數,為邊緣設備提供了更好的網絡基礎設施。物聯網則提供了大量的感測器和設備,產生了大量的數據,需要在邊緣進行處理和分析。
發佈日期:

使用Tensorboard來觀察模型效能

Tensorboard介紹

在機器學習中,要改進模型的某些參數,通常需要觀察模型數據以進行衡量。TensorBoard 是用於提供機器學習工作流期間所需測量和呈現的工具。它使您能夠跟踪實驗指標(例如損失和準確率),呈現模型計算圖,將嵌入向量投影到較低維度的空間等。

TensorBoard是TensorFlow的可視化工具,用於可視化和分析機器學習實驗的結果。它提供了一組豐富的可視化面板,可以幫助您理解、調節測試和優化訓練過程中的模型。

主要功能和用途

以下是TensorBoard的一些主要功能和用途:

  1. Scalar Dashboard(標量儀表板):顯示訓練過程中的標量指標,如損失函數、準確率、學習率等。您可以隨時跟踪這些指標間的變化,並比較不同實驗之間的結果。
  2. Graph Dashboard(圖形儀表板):顯示模型的計算圖,可視化網絡結構和層之間的連接。這有助於理解和調試模型的結構,查看各層的輸入輸出形狀。
  3. 直方圖儀表板(直方圖儀表板):顯示權重、加權和激活函數的分配情況。這用於觀察參數隨時間的變化、檢測梯度消失或突然爆炸等問題很有幫助。
  4. Image Dashboard(圖像儀表板):可視化輸入圖像、模型生成的圖像或模型層輸出的特徵圖等。這對於在圖像數據中觀察模型上的表現和理解卷積網絡中的特徵提取過程非常有用。
  5. Embedding Dashboard(嵌入儀表板):用於可視化高維嵌入空間中的數據。您可以在三維空間中探索和比較嵌入向量,以及在嵌入空間中相似的樣本。
  6. Projector Dashboard(投影儀表板):提供了一種交互式的界面,可以在高維空間中對數據進行探索和分析。您可以通過選擇和過濾樣本、聚類分析和降維等操作來理解數據集的結構。

TensorBoard 提供了一種觀察的方式來監視和分析機器學習模型,以了解模型的行為並進行調優。您可以使用 TensorFlow 的相關API(如)將數據寫入TensorBoard日誌文件,然後在命令tf.summary中行中運行 TensorBoard 來查看和分析結果。

如何使用

以下為一個範例程式

import tensorflow as tf
import datetime

mnist = tf.keras.datasets.mnist

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

def create_model():
  return tf.keras.models.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Dense(10, activation='softmax')
  ])

model = create_model()
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
# 設定log的資料夾
log_dir = "logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
# 設定tensorboard的callback
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)

model.fit(x=x_train, 
          y=y_train, 
          epochs=5, 
          validation_data=(x_test, y_test), 
          callbacks=[tensorboard_callback])

接著就可以在所指定的資料夾位置運行tensorboard介面

tensorboard --logdir logs/fit

接著開啟http://localhost:6006就可以看到如下畫面,這個畫面是Scalars,顯示損失和指標在每個時期如何變化

Graphs用以可視化模型。可以看出你的模型是如何建構的,有用到那些方法

Distributions是顯示張量隨時間的分佈。DistributionsHistograms這兩頁很相似,可了解模型權重隨時間的變化,作為判斷是否出現問題的初步衡量標準。

Histograms是使用Ridgeline plot來顯示張量隨時間的分佈,橫軸為epochs訓練的次數,縱軸則為權重和偏差的值

Time-Seriesscalars非常相似,都是在評估準確度。然而,一個區別是每次訓練迭代而不是每個時期對目標指標的觀察。

發佈日期:

如何訓練和測試數據

將資料切分

在數據科學中,訓練數據和測試數據扮演著兩個主要的角色。評估已構建模型的性能與訓練和構建模型同樣重要,因為未經評估性能的模型可能會產生錯誤的預測並導致嚴重的並發症。為了防止這種情況發生並確保預測的準確性,您必須足夠好地測試和驗證模型。

為了構建和評估機器學習模型的性能,我們通常將數據集分成兩個不同的數據集。這兩個數據集是訓練數據測試數據

訓練數據測試數據
用於構建模型用於評估構建的模型
分配更大的數據部分分配較小的數據部分
可進一步劃分以進行驗證不會進一步分割

什麼是驗證數據

驗證數據是從訓練數據中分離出來的子數據集,用於在訓練過程中驗證模型。來自驗證過程的信息幫助我們改變模型的參數、分類器以獲得更好的結果。所以基本上,驗證數據可以幫助我們優化模型。 

使用 Scikit-learn 的 train_test_split來切割數據

使用下面這段程式碼可以將訓練及分割成訓練及驗證集

from sklearn.model_selection import train_test_split
# 載入資料集
....
# 分離訓練和測試數據
X_train, X_val, y_train, y_val = train_test_split(train_images_fold, train_labels_fold, test_size=0.1, random_state=42)
model = create_model()
keras_classifier.fit(X_train, y_train, validation_data=(X_val, y_val))

使用測試資料作驗證

下面這段程式可以使用X_test、y_test來使用model做測試,並且可以用accuracy_score來取得準確率,並將準確率存入一個陣列裡

predictions = keras_classifier.predict(X_test)
accuracy = accuracy_score(y_test, predictions)
accuracy_scores.append(accuracy)

若是想要取得錯誤的資料集,則可以用np.where來取得與正確答案不一樣的資料,並將錯誤的資料存入incorrect陣列裡面

incorrect_indices = np.where(predictions != y_test)[0]
incorrect_images = X_test[incorrect_indices]
incorrect_labels = y_test[incorrect_indices]
incorrect_prediction = predictions[incorrect_indices]
for i in range(len(incorrect_indices)):
    incorrect.append({"image": incorrect_images[i] ,"label": incorrect_labels[i], "pred": incorrect_prediction[i], "idx": fold_index})

完整範例

下面的範例為結合K-Fold概念,將資料及分成五份,並做五次的訓練以判別模型的訓練狀況是否有過擬合的狀況。其中會每次會取其4分來做訓練、1份做測試,再將訓練集中的1/10拆做驗證集。最後使用matplotlib.pyplot來顯示這五次之中,測試集中錯誤結果的圖片

import numpy as np
import matplotlib.pyplot as plt
import pathlib
from sklearn.model_selection import StratifiedKFold
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score
from tensorflow import keras
from tensorflow.keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_validate

img_path = 'dice3'

def class_names(path):
    return np.array(sorted([item.name for item in pathlib.Path(path).glob('*') if
                            "LICENSE.txt" != item.name]))
def create_model():
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Rescaling(1. / 255))
    model.add(tf.keras.layers.Conv2D(32, kernel_size=7, activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D())
    model.add(tf.keras.layers.Conv2D(64, kernel_size=5, activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D())
    model.add(tf.keras.layers.Flatten())
    model.add(tf.keras.layers.Dense(64, activation='relu'))
    model.add(tf.keras.layers.Dense(len(class_names(img_path))))

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


train_ds = tf.keras.utils.image_dataset_from_directory(
  img_path,
  seed=7,
  batch_size=32)
train_images = []
train_labels = []

for images, labels in train_ds:
    train_images.append(images.numpy())
    train_labels.append(labels.numpy())

train_images = np.concatenate(train_images, axis=0)
train_labels = np.concatenate(train_labels, axis=0)
label_encoder = LabelEncoder()
train_labels_encoded = label_encoder.fit_transform(train_labels)

# 創建KerasClassifier
keras_classifier = KerasClassifier(build_fn=create_model, epochs=5, batch_size=16)

# 定義StratifiedKFold
kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=123)

fold_index = 1
# 進行交叉驗證
accuracy_scores = []
incorrect = []
for train_indices, val_indices in kfold.split(train_images, train_labels_encoded):
    print("fold_index="+str(fold_index))
    train_images_fold = train_images[train_indices]
    train_labels_fold = train_labels_encoded[train_indices]

    X_test = train_images[val_indices]
    y_test = train_labels_encoded[val_indices]
    
    # 分離訓練和測試數據
    X_train, X_val, y_train, y_val = train_test_split(train_images_fold, train_labels_fold, test_size=0.1, random_state=42)
    model = create_model()
    keras_classifier.fit(X_train, y_train, validation_data=(X_val, y_val))

    predictions = keras_classifier.predict(X_test)
    accuracy = accuracy_score(y_test, predictions)
    accuracy_scores.append(accuracy)

    incorrect_indices = np.where(predictions != y_test)[0]
    incorrect_images = X_test[incorrect_indices]
    incorrect_labels = y_test[incorrect_indices]
    incorrect_prediction = predictions[incorrect_indices]
    for i in range(len(incorrect_indices)):
        incorrect.append({"image": incorrect_images[i] ,"label": incorrect_labels[i], "pred": incorrect_prediction[i], "idx": fold_index})
    # 印出準確率
    print("Accuracy scores:", accuracy_scores)
    print("Mean accuracy:", np.mean(accuracy_scores))
    fold_index += 1

# 顯示出錯誤的答案
images_per_page = 15
num_images_per_row = 5
num_images_per_col = 3
        
num_pages = (len(incorrect) - 1) // images_per_page + 1
for page in range(num_pages):
    start_idx = page * images_per_page
    end_idx = (page + 1) * images_per_page
    page_detail = incorrect[start_idx:end_idx]
    
    fig, axes = plt.subplots(num_images_per_col, num_images_per_row, figsize=(num_images_per_col*2, num_images_per_row*2))
    i=0
    for data in page_detail:
        image = data["image"]
        label = data["label"]
        pred = data["pred"]
        idx = data["idx"]
        row = i // num_images_per_row
        col = i % num_images_per_row
        ax = axes[row, col]
        image = image.astype(np.uint8)
        ax.imshow(image)
        ax.set_title(f"{label}->{pred}({idx})")
        i = i+1
        ax.axis("off")

    plt.tight_layout()
    #plt.suptitle("fold_index:"+str(fold_index)+"-"+str(page+1), x=0, y=1, ha='left', va='top')
    plt.show()

參考資料

發佈日期:

k-Fold Cross-Validation(交叉驗證)

一般所使用的方式 – Holdout method

這是一種非常基本且簡單的方法,我們將整個數據集分為兩部分,即訓練數據測試數據。顧名思義,我們在訓練數據上訓練模型,然後在測試集上進行評估。通常,訓練數據的大小設置為測試數據的兩倍以上,因此數據按70:30或80:20的比例進行分割。

在這種方法中,數據在分割之前首先被隨機洗牌。由於模型是在不同的數據點組合上訓練的,每次訓練時模型都會給出不同的結果,這可能是不穩定的原因。此外,我們永遠無法保證我們選擇的訓練集能夠代表整個數據集。

此外,當我們的數據集不是太大時,測試數據很可能包含一些我們丟失的重要信息,因為我們沒有在測試集上訓練模型。範例程式如下:

import tensorflow as tf
 
# 建立圖片資料集
dataset = tf.keras.utils.image_dataset_from_directory(
    'image_directory',
    labels='inferred',
    class_names=None,
    label_mode='int'
)
 
# 計算資料集的大小
dataset_size = tf.data.experimental.cardinality(dataset).numpy()
 
# 計算訓練集和測試集的大小
test_size = int(0.2 * dataset_size)
train_size = dataset_size - test_size
 
# 將資料集分割為訓練集和測試集
train_dataset = dataset.take(train_size)
test_dataset = dataset.skip(train_size)

改善方法 – k-Fold Cross-Validation

K 折交叉驗證是改進的一種方法。這種方法保證了我們模型的分數不依賴於我們選擇訓練集和測試集的方式。將數據集分為 k 個子集,並將保留方法重複 k 次。讓我們分步驟完成這個過程:

  1. 將整個數據集隨機分成 k 個折疊(子集)
  2. 對於數據集中的每個折疊,在數據集的 k – 1 個折疊上構建模型。然後,測試模型以檢查第 k 次折疊的有效性
  3. 重複此操作,直到每個 k 重都作為測試集
  4. k 記錄的準確度的平均值稱為交叉驗證準確度,並將作為模型的性能指標。

因為它確保來自原始數據集的每個觀察結果都有機會出現在訓練和測試集中,所以與其他方法相比,這種方法通常會產生偏差較小的模型。如果我們的輸入數據有限,這是最好的方法之一。 

這種方法的缺點是訓練算法必須從頭開始重新運行 k 次,這意味著進行評估需要 k 倍的計算量。

分層k-Fold – Stratified K Fold Cross Validation

在分類問題上使用 K Fold 可能會很棘手。由於我們隨機打亂數據,然後將其劃分為折疊,因此我們可能會得到高度不平衡的折疊,這可能會導致我們的訓練出現偏差。例如,讓我們以某種方式得到一個折疊,其中大多數屬於一個類(例如正類),而只有少數屬於負類。這肯定會破壞我們的訓練,為了避免這種情況,我們使用分層進行分層折疊。

分層是重新排列數據的過程,以確保每次折疊都能很好地代表整體。例如,在每個類包含 50% 數據的二元分類問題中,最好對數據進行排列,使得在每個折疊中,每個類包含大約一半的實例。

下面這張圖,是在分類問題上使用K-Fold折疊的方式,會發現因為不同分類隨機分組導致每個分組的數量不一,而破壞訓練的狀況

資料狀況: 共三種資料,每一種70張,共210張,訓練集168張,測試集42張

而改使用StratifiedKFold去做資料分組之後,能看到訓練過程更加順利

範例程式

以下為使用Tensorflow加上sklearn.model_selection去做資料分組的測試程式

import numpy as np
import pathlib
import tensorflow as tf
from sklearn.model_selection import LeaveOneOut,KFold
from sklearn.model_selection import StratifiedKFold
from sklearn.preprocessing import LabelEncoder
from sklearn.datasets import load_iris

img_path = 'dice3'
train_ds = tf.keras.utils.image_dataset_from_directory(
  img_path,
  seed=7,
  batch_size=32)
X = []
y = []

for images, labels in train_ds:
    X.append(images.numpy())
    y.append(labels.numpy())

X = np.concatenate(X, axis=0)
y = np.concatenate(y, axis=0)
label_encoder = LabelEncoder()
y = label_encoder.fit_transform(y)

def class_names(path):
    return np.array(sorted([item.name for item in pathlib.Path(path).glob('*') if
                            "LICENSE.txt" != item.name]))
def create_model():
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Rescaling(1. / 255))
    model.add(tf.keras.layers.Conv2D(32, kernel_size=7, activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D())
    model.add(tf.keras.layers.Conv2D(64, kernel_size=5, activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D())
    model.add(tf.keras.layers.Flatten())
    model.add(tf.keras.layers.Dense(64, activation='relu'))
    model.add(tf.keras.layers.Dense(len(class_names(img_path))))
    model.compile(
        optimizer='adam',
        loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
        metrics=['accuracy'])
    return model

def make_dataset(X_data,y_data,n_splits):
    def gen():
        for train_index, test_index in KFold(n_splits).split(X_data):
            X_train, X_test = X_data[train_index], X_data[test_index]
            y_train, y_test = y_data[train_index], y_data[test_index]
            yield X_train,y_train,X_test,y_test

    return tf.data.Dataset.from_generator(gen, (tf.float64,tf.float64,tf.float64,tf.float64))

dataset=make_dataset(X,y,5)


for X_train,y_train,X_test,y_test in dataset:
    print(len(X_train), len(y_train), len(X_test), len(y_test))
    model = create_model()
    model.fit(X_train, y_train, epochs=10, batch_size=32, validation_data=(X_test, y_test))


若要改成使用分層K-Fold,則要使用sklearn.model_selection的StratifiedKFold

def make_dataset(X_data,y_data,n_splits):
    def gen():
        kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=123)
        for train_index, test_index in kfold.split(X_data, y_data):
            X_train, X_test = X_data[train_index], X_data[test_index]
            y_train, y_test = y_data[train_index], y_data[test_index]
            yield X_train,y_train,X_test,y_test

    return tf.data.Dataset.from_generator(gen, (tf.float64,tf.float64,tf.float64,tf.float64))

資料集長這樣

參考資料

發佈日期:

Optimization loop failed: Cancelled: Operation was cancelled

遇到的錯誤訊息

2023-06-14 16:32:39.652288: W tensorflow/core/data/root_dataset.cc:167] Optimization loop failed: Cancelled: Operation was cancelled

這個錯誤訊息代表TensorFlow 在優化過程中遇到了問題,並且操作被取消了。當操作被取消時,TensorFlow 無法完成所需的計算任務,因此無法產生期望的結果。具體來說,如果你在使用 TensorFlow 的 CPU 模式下運行一個優化循環,並且該循環被取消,以下情況可能發生:

  • 訓練過程中斷:如果你正在訓練一個模型,操作取消將導致訓練過程中止。你將無法完成整個訓練過程,無法獲得最終訓練好的模型。
  • 中斷的結果:在某些情況下,如果操作被取消,TensorFlow 可能會嘗試返回已經計算出的部分結果。這取決於具體的操作和中斷發生的時間點。然而,這些部分結果通常不是完整或不可用的。
  • 推斷錯誤:如果你正在使用模型進行推斷(如輸入一條文本獲取其分類),而操作被取消,你將無法獲得模型對輸入的預測結果。這可能導致你無法得知模型的輸出,或者輸出結果不完整、不准確。

為什麼會出現此錯誤

大部分的狀況是因為資源不足,導致運算被中斷

  • 計算資源不足:TensorFlow 在 CPU 模式下運行時,對於大型、複雜的模型或數據集,可能需要較長的時間來完成計算。如果計算資源有限,操作可能會被取消。
  • 內存不足:TensorFlow 在處理大型模型或數據時需要大量內存。如果內存不足,操作可能會被取消。
  • 配置錯誤:有時,TensorFlow 的配置可能不正確,導致操作無法成功完成。這可能包括錯誤的版本或依賴問題。

觀察電腦CPU與MEMORY使用狀況

要解決此問題,首先要先觀察是哪一部分出了問題,就需要在程式內去監控資源的使用。請參考此篇教學: How to get current CPU and RAM usage in Python

    # Importing the library
    import psutil


    # Getting % usage of virtual_memory ( 3rd field)
    if psutil.virtual_memory()[2] > 80:
        print(time.strftime("%Y-%m-%d_%H-%M-%S") + ' RAM memory % used:', psutil.virtual_memory()[2])
        # Getting usage of virtual_memory in GB ( 4th field)
        print('RAM Used (GB):', psutil.virtual_memory()[3]/1000000000)
    
    cpu_usage = psutil.cpu_percent()
    if cpu_usage > 80:
        print(time.strftime("%Y-%m-%d_%H-%M-%S") + ' The CPU usage is: ', cpu_usage)

解決 – 若是使用太多記憶體

在Tensorflow裡面有一篇討論串,滿多人都會遇到此問題,其中一位大大建議在GPU上提高可使用的內存,可解決此問題

gpus = tf.config.experimental.list_physical_devices('GPU')
tf.config.experimental.set_memory_growth(gpu, True)

討論串: Optimization loop failed: Cancelled: Operation was cancelled

另外也可以考慮設置合適的 batch_size、限制模型大小或減少數據集規模是一種在沒有 GPU 或內存受限的情況下控制內存使用的方法。下面是一些方法可以考慮:

  • 調整 batch_size:batch_size 是每次模型訓練時用於處理的樣本數量。較大的 batch_size 可能會導致內存不足。你可以嘗試降低 batch_size 的值,以減少內存需求。但要注意,較小的 batch_size 可能會影響訓練過程的穩定性和收斂速度。
  • 限制模型的大小:模型的大小直接影響內存使用。你可以嘗試以下方法來限制模型的大小:
    • 減少模型的層數或參數數量。
    • 使用輕量級模型架構,例如 MobileNet、SqueezeNet 等。
    • 使用模型剪枝技術,減少模型中冗餘參數的數量。
    • 使用低精度(如 16 位浮點數)表示模型的參數,以減少內存佔用。
  • 減少數據集規模:如果數據集過大而導致內存不足,你可以考慮以下方法來減少數據集的規模:
    • 隨機採樣:從原始數據集中隨機選擇一部分樣本作為子集進行訓練。確保採樣後的數據集仍具有代表性。
    • 數據集切片:將大型數據集切割成多個較小的部分,逐個部分進行訓練。可以使用交叉驗證等技術來利用所有數據並評估模型性能。

解決 – 若是使用太多CPU

可嘗試以下方法來減少 CPU 的消耗:

  • 減少數據的處理量:優化輸入數據的大小和復雜度,可以減少 CPU 的負載。例如,可以嘗試縮小圖像尺寸、減少輸入序列的長度或降低數據的維度。
  • 使用批處理預測:通過將多個樣本組成一個批次進行預測,可以減少每個樣本的計算和內存佔用。 Model.predict() 方法默認可以處理批量輸入,你可以將多個樣本一起傳遞給該方法進行預測。
  • 並行處理:如果你的系統支持多線程或多進程,並且你的模型和數據可以進行並行處理,可以使用並行化方法來提高 CPU 的利用率。例如,使用 Python 的 multiprocessing 模塊或 TensorFlow 的 tf.data.Dataset 的並行化功能。
  • 優化模型:檢查你的模型結構和計算圖,看是否有可以優化的部分。可能存在一些冗餘計算或可以簡化的操作。你可以嘗試使用 TensorFlow 的圖優化工具,如 tf.function 和 tf.autograph,來加速模型的計算過程。
  • 使用更高效的庫或算法:某些情況下,使用其他庫或算法可能比 TensorFlow 更高效。你可以嘗試使用其他機器學習庫,如 PyTorch 或 Scikit-learn,並根據你的需求選擇更適合的庫和算法。
  • 升級硬件:如果你的計算機配置允許,可以考慮升級到更強大的 CPU 或添加更多的 CPU 核心。這樣可以提高系統的並行處理能力和整體性能。
發佈日期:

計算兩個點之間的直線距離

使用math.hypot

math.hypot 是 Python 內置的數學模塊 math 中的函數。它接受兩個參數,分別代表兩點的 x 和 y 坐標差值,然後返回它們的歐幾里德距離(即直線距離)。

import math

x1, y1 = 1, 2
x2, y2 = 3, 4

distance = math.hypot(x2 - x1, y2 - y1)
print(distance)

使用np.sqrt

np.sqrt 是 NumPy 庫中的函數,用於計算給定數值的平方根。要使用 np.sqrt 計算兩點之間的距離,你需要首先計算兩點在 x 和 y 坐標軸上的差值的平方和,然後將它們相加,再使用 np.sqrt 對結果進行平方根運算。

import numpy as np

x1, y1 = 1, 2
x2, y2 = 3, 4

distance = np.sqrt((x2 - x1)**2 + (y2 - y1)**2)
print(distance)

發佈日期:

對輪廓的點做旋轉計算

使用角度的正弦和餘弦函數,將長方形的寬度和高度乘以正確的係數,以獲得旋轉後的角點座標。

下面為一個將一個長方形的四個點作45度旋轉的簡單範例:

import cv2
import numpy as np
import math

base_x = 100
base_y = 100
width = 100
height = 50
angle = 45

# 計算長方形中心點座標
center_x = base_x + (width // 2)
center_y = base_y + (height // 2)

# 將角度轉換為弧度
angle_rad = math.radians(angle)

# 計算長方形四個角的相對座標
cos_val = math.cos(angle_rad)
sin_val = math.sin(angle_rad)
x = width / 2
y = height / 2

# 計算四個角點座標
point1 = (int(center_x - x * cos_val + y * sin_val), int(center_y - x * sin_val - y * cos_val))
point2 = (int(center_x + x * cos_val + y * sin_val), int(center_y + x * sin_val - y * cos_val))
point3 = (int(center_x + x * cos_val - y * sin_val), int(center_y + x * sin_val + y * cos_val))
point4 = (int(center_x - x * cos_val - y * sin_val), int(center_y - x * sin_val + y * cos_val))

print("Point 1:", point1)
print("Point 2:", point2)
print("Point 3:", point3)
print("Point 4:", point4)

# 創建空白影像
image = np.zeros((500, 500, 3), dtype=np.uint8)

# 轉換座標為Numpy陣列
pts = np.array([point1, point2, point3, point4], dtype=np.int32)

# 繪製多邊形
cv2.polylines(image, [pts], True, (0, 255, 0), thickness=2)

# 顯示結果
cv2.imshow('Rectangle', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

顯示結果如下

發佈日期:

點到多邊形的最短距離

cv2.pointPolygonTest是OpenCV中的一個函數,用於計算點到多邊形的最短距離或點是否在多邊形內。

函數的語法如下:

_, intersection = cv2.pointPolygonTest(rect3, tuple(line1_midpoint), measureDist=False)
  • contour:多邊形的輪廓,可以是Numpy陣列或OpenCV的輪廓物件。
  • point:要計算距離的點,通常是一個(x, y)座標元組。
  • measureDist:指定是否計算點到多邊形的最短距離。如果為True,則返回距離值;如果為False,則返回一個整數值表示點的位置關係:正數表示點在多邊形內部、負數表示點在多邊形外部、0表示點在多邊形邊界上。

相關函數請參考: cv2.distanceTransform

另外要畫出多邊形可使用cv2.polylines,如以下範例

import cv2
import numpy as np

# 定義長方形的四個角點
rect1 = np.array([[100, 100], [300, 100], [300, 200], [100, 200]])

# 創建空白影像
image = np.zeros((500, 500, 3), dtype=np.uint8)

# 繪製長方形
cv2.polylines(image, [rect1], True, (0, 255, 0), thickness=2)

# 顯示結果
cv2.imshow('Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

發佈日期:

使用OpenCV將圖形轉正

旋轉圖片的方法

若是單純只是要把圖片做角度的旋轉,可以直接使用OpenCV 的 cv2.rotate() 函数。可按指定的方向旋轉圖像。如下:

import cv2

# 讀取圖像
image = cv2.imread('your_image.jpg')

# 將圖像旋轉90度
rotated_image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)

# 顯示旋轉後的圖像
cv2.imshow('Rotated Image', rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

翻轉圖片的方法

cv2.flip() 是 OpenCV 中用於圖像翻轉的函數。它可以在水平、垂直或兩個方向上翻轉圖像。該函數接受三個參數:輸入圖像、翻轉的模式和輸出圖像的可選參數。

dst = cv2.flip(src, flipCode[, dst])

flipCode:翻轉的模式。可以是以下值之一:

  • 0:水平翻轉(沿著垂直軸翻轉)。
  • 1:垂直翻轉(沿著水平軸翻轉)。
  • -1:同時在水平和垂直方向上翻轉。

cv2.flip() 函數和 cv2.rotate() 函數都可以用於實現圖像的旋轉和翻轉,但它們的效果是不同的。

cv2.flip() 函數可以在水平和垂直方向上翻轉圖像,包括水平翻轉、垂直翻轉和同時在水平和垂直方向上翻轉。例如,使用 cv2.flip(image, -1) 可以同時在水平和垂直方向上翻轉圖像。

cv2.rotate() 函數用於對圖像進行旋轉。通過指定旋轉的角度和旋轉中心點,可以實現不同角度的旋轉。例如,使用 cv2.rotate(image, cv2.ROTATE_180_CLOCKWISE) 可以將圖像順時針旋轉180度。

雖然cv2.flip(image, -1)cv2.rotate(image, cv2.ROTATE_180_CLOCKWISE) 可以實現類似的效果,將圖像翻轉或旋轉180度,但它們的內部操作是不同的。 cv2.flip() 是基於軸對稱翻轉實現的,而 cv2.rotate() 是基於旋轉變換實現的。

針對形狀做角度校正

在許多圖像偵測的狀況,我們仍然會需要針對物件去做旋轉,首先我們一定是先用cv2.findContours取得輪廓,然後取得該物件輪廓的角度。這邊很重要的,就是要取得物件輪廓的角度,要取得角度,首先就要先去做輪廓擬合(請參考: OpenCV裡面形狀擬合的幾種方法)。

這邊我大推使用橢圓去做輪廓擬合並且取得軸心的角度,為什麼呢? 雖然cv2.minAreaRect() 可計算最小擬合矩形,但是這個矩形會非常容易受到輪廓的些微影響而改變擬合的方式,例如以下圖為例,就有可能有黑框、紅色框兩種的最小擬合矩形(會視當下輪廓取得的細微變化而改變)。也因此所取得的角度會非常多變,後續的辨識也會更困難

但是使用最小擬合橢圓,對於像上面這種左右、上下為對稱,但是長寬不同的形狀來說,非常適合使用最小擬合橢圓cv2.fitEllipse(),使用範例如下

import cv2

image = cv2.imread('./333_2023-06-08_19-57-30.jpg')
canny = cv2.Canny(image , 50, 250)
cnts, hier = cv2.findContours(canny , cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 執行最小橢圓擬合
ellipse = cv2.fitEllipse(cnts[0])
(center, axes, angle) = ellipse
cv2.ellipse(image, ellipse, (0, 255, 0), 2)
# 顯示結果
cv2.imshow('image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

上面可以看到(center, axes, angle) = ellipse這邊的angle就是所偵測到的輪廓的角度,接著可以用cv2.warpAffine方法將圖像轉正

def rotatedDice(image, cnt):
    # 取得最小擬合橢圓並對圖像做翻轉
    ellipse = cv2.fitEllipse(cnt)
    (center, axes, angle) = ellipse
    angle = angle + 90
    rotation_matrix = cv2.getRotationMatrix2D(tuple(center), angle, 1)
    image = cv2.warpAffine(image, rotation_matrix,(image.shape[1], image.shape[0]))
    # 計算裁切位置
    mark = np.zeros_like(image)
    cv2.drawContours(mark, [cnt], 0, (255, 255, 255), -1)
    mark = cv2.warpAffine(mark, rotation_matrix,(mark.shape[1], mark.shape[0]))
    mark = cv2.cvtColor(mark, cv2.COLOR_RGB2GRAY)
    cnts, hier = cv2.findContours(mark, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    x, y, w, h = cv2.boundingRect(cnts[0])
    matting_result = image[y:y+h,x:x+w,:]
    return matting_result

從上面,我們使用cv2.warpAffine來做圖片角度的校正,warpAffine裡面有要輸入一個旋轉的矩陣的參數,在上面的範例,我們使用cv2.getRotationMatrix2D,這個參數是單純做形狀旋轉,但是在真實的世界當中,大部分3D的角度轉換也會帶有著深度的轉換,如下圖

這時候就會需要使用cv2.getAffineTransform來取得這個旋轉矩陣

import cv2
import numpy as np

# 定義三個點的坐標
point1 = (106, 92)
point2 = (28, 91)
point3 = (154, 33)

# 定義旋轉角度
rotation_angle = -45

# 創建一個空白圖像
image = np.zeros((500, 500), dtype=np.uint8)

# 在圖像上繪製三角形
cv2.drawContours(image, [np.array([point1, point2, point3])], 0, (255), thickness=2)

# 計算旋轉中心
center = np.mean([point1, point2, point3], axis=0)

# 構建旋轉矩陣
rotation_matrix = cv2.getRotationMatrix2D(tuple(center), rotation_angle, 1)

# 對整個圖像進行旋轉
rotated_image = cv2.warpAffine(image, rotation_matrix, (image.shape[1], image.shape[0]))

# 顯示旋轉後的圖像
cv2.imshow("Rotated Image", rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
發佈日期:

OpenCV魔術棒填充顏色

函數介紹

cv2.floodFill() 函數可以用來對圖像進行泛洪填充。泛洪填充是指將圖像中指定的像素點及其相連的像素點填充成指定的顏色。它通常用於圖像的背景去除、圖像分割等應用中。常用的場景如下:

  1. 圖像分割:可以使用泛洪填充來將圖像分割成不同的區域,例如可以從圖像中自動分離出前景和背景。
  2. 圖像去噪:可以使用泛洪填充來去除圖像中的噪聲,例如在二值化圖像中可以填充噪點附近的像素,使其與周圍的像素保持一致。
  3. 圖像修復:可以使用泛洪填充來修復圖像中的缺陷,例如在圖像中填充缺陷周圍的像素,使其與周圍的像素保持一致。
  4. 圖像標記:可以使用泛洪填充來對圖像進行標記,例如對圖像中的區域進行標記,或者在圖像中添加文字等。

總之,floodFill是一種非常實用的圖像處理技術,可以在很多場合下使用,並且可以通過調整填充的參數來達到不同的效果。

參數介紹

cv2.floodFill() 函數的常用參數如下:

cv2.floodFill(image, mask, seedPoint, newVal[, rect[, loDiff[, upDiff[, flags]]]]) -> retval, image, mask, rect
  • image:要填充的圖像,必須為8位、單通道或三通道影像。如果是三通道影像,則只有當 flags 參數中包含 cv2.FLOODFILL_FIXED_RANGE 時,填充才會基於每個像素的三通道值。
  • mask:用於指定填充區域的填充標記,必須為單通道、8位或32位浮點數影像,大小應比 image 多2個像素。如果填充標記中對應位置的值為0,則該像素將不會被填充。如果該參數為 None,則會自動創建一個和 image 大小相同的標記。
  • seedPoint:種子點的位置,是一個二元數組 (x, y)
  • newVal:填充的新值,可以是一個標量或一個三元數組 (B, G, R)
  • rect:可選的輸出參數,用於返回填充區域的最小矩形。
  • loDiff:可選的最小差值,如果當前像素和種子點之間的差值小於 loDiff,則這個像素將被填充。默認值為0。
  • upDiff:可選的最大差值,如果當前像素和種子點之間的差值大於 upDiff,則這個像素不會被填充。默認值為0。
  • flags:可選的填充標誌,可以是以下幾種取值之一或者它們的組合:
    • cv2.FLOODFILL_FIXED_RANGE:基於每個像素的三通道值來填充,默認基於灰度值。
      • cv2.FLOODFILL_MASK_ONLY:僅修改填充標記,不修改圖像。
        • cv2.FLOODFILL_MULTISCALE:使用多個尺度進行填充。
          • cv2.FLOODFILL_POINT:表示 seedPoint 參數為像素的坐標,而不是像素值。

使用範例

import cv2
import numpy as np

# 讀入圖像,轉為灰度
img = cv2.imread('image.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 找到種子點
seed_point = (100, 100)

# 設置填充顏色和填充標記
fill_color = (0, 0, 255)
fill_mask = np.zeros((gray.shape[0]+2, gray.shape[1]+2), dtype=np.uint8)

# 泛洪填充
cv2.floodFill(img, fill_mask, seed_point, fill_color)

# 顯示圖像
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()