---
# System prepended metadata

title: Python 程式設計入門筆記
tags: [python]

---

# Python 程式設計入門筆記之二

這份筆記是成功大學「Python程式設計入門」的課程筆記整理，紀錄10/17-11/14之課程內容。

## Lecture5：運用Python進行科學計算

### 1. 本利和計算：
本利和是一種財務計算，用於計算存款或貸款在一定時間內的總金額。它基於利率和時間，包括兩個主要部分：本金（Principal）和利息（Interest）。本金是最初存入的金額，而利息是根據利率計算的附加金額。

以下是計算本利和的一個基本公式：
$A=P\cdot(1+{r \over n})^{nt}$

* $A$：本利和（總金額）
* $P$：本金（最初存入的金額）
* $r$：年利率（以小數形式表示，如5%表示為0.05）
* $n$：每年計算利息的次數（通常為1，表示一年一次）
* $t$：存款或貸款的年數

以下是一個Python函數示例，用於計算本利和：

```python=
# 輸入資訊
P = 1000  # 假設本金為1000元
r = 0.03  # 年利率為3%
n = 365   # 每天複利一次
t = 1     # 1年

# 計算本利和
A = P * (1 + r/n)**(n*t)

# 輸出結果
print("一年後的本利和為:", A)
```
:::success
會印出：一年後的本利和為: 1030.453263600551
:::

以下介紹有四捨五入與無四捨五入的差異：

```python=
# 輸入資訊
P = 1000  # 假設本金為1000元
r = 0.03  # 年利率為3%
n = 365   # 每天複利一次
t = 1     # 1年

# 計算每日利息和本利和（有四捨五入）
A_with_rounding = P
for _ in range(n*t):
    daily_interest = A_with_rounding * (r/n)
    A_with_rounding += daily_interest
    A_with_rounding = round(A_with_rounding)

# 計算每日利息和本利和（無四捨五入）
A_without_rounding = P
for _ in range(n*t):
    daily_interest = A_without_rounding * (r/n)
    A_without_rounding += daily_interest

# 輸出結果
print("有四捨五入的本利和為:", A_with_rounding)
print("無四捨五入的本利和為:", A_without_rounding)

# 比較兩者差異
difference = A_with_rounding - A_without_rounding
print("有四捨五入與無四捨五入的差異為:", difference)
```

:::success
會印出：
有四捨五入的本利和為: 1000
無四捨五入的本利和為: 1030.45326360051
有四捨五入與無四捨五入的差異為: -30.4532636005099
:::

可以看見有無四捨五入差異相當巨大，因此我們不能夠輕忽。

### 2. 人口增長模擬：
```python=
import numpy as np
import matplotlib.pyplot as plt

# 定義參數
r = 0.1  # 增長率
M = 500  # 環境容量
u0 = 10  # 初始人口大小
num_steps = 100  # 模擬的時間步數

# 初始化數據
u_values = [u0]
time_values = [0]

# 進行數值模擬
for t in range(1, num_steps + 1):
    u_t = u_values[-1]
    u_next = u_t + r * u_t * (1 - u_t/M)
    u_values.append(u_next)
    time_values.append(t)

# 繪製人口變化圖
plt.plot(time_values, u_values)
plt.xlabel('time')
plt.ylabel('population')
plt.title('Logistic model')
plt.grid(True)
plt.show()
```
會跑出以下圖表：
![](https://hackmd.io/_uploads/Hy3kmOiZa.png)

這是一個[S型函數](https://zh.wikipedia.org/zh-tw/S%E5%9E%8B%E5%87%BD%E6%95%B0)，常見於AI人工智慧的研究中。

## Lecture6：運用Python進行網路爬蟲

### 1. 安裝所需的庫：
首先，你需要安裝Python的相關庫，最常用的是 requests 用於發送HTTP請求，以及 Beautiful Soup 或 Scrapy 用於解析和處理網頁內容。你可以使用pip進行安裝：

```python=
pip install requests
pip install beautifulsoup4
```

### 2. 發送HTTP請求：
使用 requests 庫，你可以發送HTTP GET或POST請求到目標網站。這將幫助你下載網頁內容，以便後續分析。例如：

```python=
import requests

url = 'https://example.com'
response = requests.get(url)
```

### 3. 解析網頁內容：

使用 Beautiful Soup 或其他HTML解析庫來解析下載的網頁內容。這將使你能夠尋找和提取特定的元素，如超連結、文本、圖像等。舉例來說：

```python=
from bs4 import BeautifulSoup

soup = BeautifulSoup(response.text, 'html.parser')
```

### 4. 定位和提取數據：

透過解析後的HTML，你可以使用**CSS選擇器**或**XPath表達式**來定位和提取感興趣的數據。例如，使用 **.find_all()** 或 **.find()** 方法尋找特定標籤或類別的元素。

### 5. 數據存儲：

一旦提取了所需的數據，你可以將其存儲在文件中，例如**CSV、JSON、或數據庫**中，以便進一步分析或後續使用。

### 6. 簡易程式範例：

請自行到成大首頁，抓取每一個成大快訊(應該有八個)的文字內容。

```python=
import requests
from bs4 import BeautifulSoup

# 指定目標網頁的URL
url = 'https://www.ncku.edu.tw/'

# 發送HTTP請求，獲取網頁內容
response = requests.get(url)

# 檢查是否成功獲取網頁內容
if response.status_code == 200:
    # 使用Beautiful Soup解析網頁內容
    soup = BeautifulSoup(response.text, 'html.parser')

    elements_1 = soup.find_all('div', class_='mouter')

    # 打印找到的元素內容
    k = 1
    for element in elements_1:

      k = k + 1
      elements_2 = element.find_all('div', class_='d-item v-it col-sm-3')
      for e in elements_2:
        if k == 5:
          print(e.text.strip())
else:
    print('無法獲取網頁內容')
```
:::success
* **import requests** 和 **from bs4 import BeautifulSoup**：這兩行程式碼匯入所需的Python庫。requests庫用於發送HTTP請求，BeautifulSoup 用於解析HTML內容。

* url = 'https://www.ncku.edu.tw/'：這行程式碼指定了目標網頁的URL，即國立成功大學的官方網站。

* **response = requests.get(url)**：這行程式碼向指定的URL發送HTTP GET請求，然後將響應存儲在 response 變數中。

* if response.status_code == 200：這行程式碼檢查是否成功獲取網頁內容。**HTTP狀態碼200表示成功。**

* 如果成功獲取網頁內容，程式將使用Beautiful Soup解析網頁內容：

* soup = BeautifulSoup(response.text, 'html.parser')：這行程式碼使用**Beautiful Soup**解析HTTP響應的文本內容。

* elements_1 = soup.find_all('div', class_='mouter')：這行程式碼查找所有具有 class 屬性為 'mouter' 的 < **div** >元素，這些元素通常包含所需的資訊。

* 接下來，程式遍歷 elements_1 中的元素，使用變數 k 來記錄元素的索引：

* elements_2 = element.find_all('div', class_='d-item v-it col-sm-3')：這行程式碼在每個 element 中查找具有 class 屬性為 'd-item v-it col-sm-3' 的 < **div** >元素，通常包含所需資訊。

* if k == 5:：這行程式碼檢查 k 是否等於5，如果是，則執行下面的程式區塊。這可能是因為你只想打印第五個 elements_1 元素中的資訊。

* **print(e.text.strip())**：這行程式碼打印找到的 elements_2 元素的文本內容，使用 **strip() 去除多餘的空格和換行符**。

* 如果無法成功獲取網頁內容（HTTP狀態碼不是200），程式將打印 '無法獲取網頁內容'，表示無法連接到指定網頁。
:::
總結，這段程式碼用於爬取指定網頁的內容，查找特定類名為 'mouter' 的 < **div** >元素，並在第五個這樣的元素中查找類名為 **'d-item v-it col-sm-3' 的子元素**，並將其文本內容打印出來。
    
## Lecture7：運用Python進行圖像處理

### 1. 下載一張圖片到image這個變數：
    
```python=
import requests
from PIL import Image, ImageEnhance
from io import BytesIO
import matplotlib.pyplot as plt

def download_image(url):
    response = requests.get(url)
    image = Image.open(BytesIO(response.content))
    return image

# 替換成你的圖片網址
image_url = "https://web.ncku.edu.tw/var/file/0/1000/img/DSC01181ok.jpg"

image = download_image(image_url)
```
    
### 2.將兩張圖合併起來的函數：
```python=
def concatenate_images(image1, image2):    # 要把兩張圖, 左右合併在一起的函式
    total_width = image1.width + image2.width  # 因為要左右合併在一起, 所以圖片的寬度
    max_height = max(image1.height, image2.height) # 兩張圖可能不一樣高, 取比較高的圖片高度

    new_image = Image.new('RGB', (total_width, max_height)) # 我不知道 'RGB' 做甚麼的,
    # 但是我看得出來新的圖片用 total_width, max_height
    new_image.paste(image1, (0, 0)) # 貼第一張圖, # 我也不知道寫 (0,0) 幹甚麼 # 左上角
    new_image.paste(image2, (image1.width, 0)) # 貼第二張圖, # 我也不知道寫 (image1.width,0) 幹甚麼

    return new_image
```
* 類似效果：
![1](https://hackmd.io/_uploads/rksG_WvV6.png)

### 3.調整圖片飽和度的函數：
```python=
# 創建一個顏色增強器對象
color_enhancer = ImageEnhance.Color(image)
# 調整顏色飽和度（1.0 表示原始飽和度，小于 1.0 減弱飽和度，大于 1.0 增强飽和度）
enhanced_image = color_enhancer.enhance(2.0)  # 增強顏色飽和度

combined_image = concatenate_images(image, enhanced_image)
plt.imshow(combined_image)
plt.axis('off')  # 不顯示座標軸
plt.show()
```
* 類似效果：
![2](https://hackmd.io/_uploads/HJUUdbvVp.png)


### 4.可以取反RGB的函數：
```python=
# 取得影像的寬度和高度
width, height = image.size

# 建立一個與原始影像大小相同的新空白影像
negative_image = Image.new("RGB", (width, height))

# 迭代每個像素並反轉 RGB 值
for x in range(width):
    for y in range(height):
        pixel = image.getpixel((x, y))  # 取得像素值元組(R,G,B)
        inverted_pixel = tuple(255 - value for value in pixel)
        # 使用 for 迴圈產生元組中的每個值
        negative_image.putpixel((x, y), inverted_pixel)  # 將 (RGB) 像素放回影像中

combined_image = concatenate_images(image, negative_image)
plt.imshow(combined_image)
plt.axis('off')  # 不顯示座標軸
plt.show()
```
* 類似效果：
![3](https://hackmd.io/_uploads/HkY_dbvET.png)

    
## Lecture8：運用Python進行遊戲設計

### 1. 井字遊戲：
```python=
# 設計井字遊戲需要以下的函數
#  印出井字盤
#  檢查贏家
#  主程式

def 印出井字盤(井字盤):
    print(井字盤[0] + '|' + 井字盤[1] + '|' + 井字盤[2])
    print('-+-+-')
    print(井字盤[3] + '|' + 井字盤[4] + '|' + 井字盤[5])
    print('-+-+-')
    print(井字盤[6] + '|' + 井字盤[7] + '|' + 井字盤[8])

def 檢查贏家(井字盤, 符號):
    贏的組合 = [
        [0, 1, 2], [3, 4, 5], [6, 7, 8],  # 水平
        [0, 3, 6], [1, 4, 7], [2, 5, 8],  # 垂直
        [0, 4, 8], [2, 4, 6]  # 對角線
    ]
    for 組合 in 贏的組合:
        if 井字盤[組合[0]] == 井字盤[組合[1]] == 井字盤[組合[2]] == 符號:
            return True
    return False

def 主程式():
    井字盤 = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
    玩家 = 'X'
    輸贏平局 = None

    # while Ture = 一直跑下去
    while True:
        印出井字盤(井字盤)
        輸入位置 = input(f'請玩家 {玩家} 輸入位置（1-9）：')

        if 輸入位置.isdigit() and 1 <= int(輸入位置) <= 9:
            輸入位置 = int(輸入位置) - 1
            if 井字盤[輸入位置] == 'X' or 井字盤[輸入位置] == 'O':
                print('該位置已被佔據，請重新輸入。')
            else:
                井字盤[輸入位置] = 玩家
                if 檢查贏家(井字盤, 玩家):
                    印出井字盤(井字盤)
                    print(f'恭喜玩家 {玩家} 獲勝！')
                    輸贏平局 = '贏'
                    break  # 跳出while-loop
                elif all(位置 == 'X' or 位置 == 'O' for 位置 in 井字盤):
                    印出井字盤(井字盤)
                    print('平局！')
                    輸贏平局 = '平局'
                    break  # 跳出while-loop
                玩家 = 'O' if 玩家 == 'X' else 'X'
        else:
            print('輸入無效，請輸入1到9之間的數字。')

    if 輸贏平局 is None:
        print('遊戲結束。')

if __name__ == '__main__':
    主程式()
```
### 2. 踩地雷：

```python=
import random

中文數字 = ['０', '１', '２', '３', '４', '５', '６', '７', '８', '９']

def 創建地雷地圖(行數, 列數, 地雷數):
    地雷地圖 = [['Ｏ' for _ in range(列數)] for _ in range(行數)]
    隨機地雷位置 = random.sample(range(行數 * 列數), 地雷數)

    for 位置 in 隨機地雷位置:
        行 = 位置 // 列數
        列 = 位置 % 列數
        地雷地圖[行][列] = 'Ｘ'

    return 地雷地圖

def 顯示遊戲地圖(遊戲地圖):
    for 行 in 遊戲地圖:
        print(' '.join(行))

def 檢查合法性(行, 列, 行數, 列數):
    return 0 <= 行 < 行數 and 0 <= 列 < 列數

def 計算周圍地雷數(地雷地圖, 行, 列):
    周圍地雷數 = 0
    鄰居方向 = [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (1, -1), (-1, 1)]

    for dx, dy in 鄰居方向:
        新行, 新列 = 行 + dx, 列 + dy
        if 檢查合法性(新行, 新列, len(地雷地圖), len(地雷地圖[0])) and 地雷地圖[新行][新列] == 'Ｘ':
            周圍地雷數 += 1

    return 周圍地雷數

def 檢查地雷(地雷地圖, 行, 列):
    if 地雷地圖[行][列] == 'Ｘ':
        return True
    return False

def 顯示地雷地圖(地雷地圖):
    for 行 in 地雷地圖:
        print(' '.join(行))

def 主程式():
    print('歡迎來到踩地雷遊戲！')
    行數 = int(input('請輸入行數：'))
    列數 = int(input('請輸入列數：'))
    地雷數 = int(input('請輸入地雷數：'))

    地雷地圖 = 創建地雷地圖(行數, 列數, 地雷數)
    遊戲地圖 = [['□' for _ in range(列數)] for _ in range(行數)]

    顯示遊戲地圖(遊戲地圖)

    生命值 = 5 #設定生命值，你可以設定成超大，這樣你就不會死了。

    while True:
        位置 = input('請輸入要檢查的位置 (例如: 1,3)：')
        try:
            行, 列 = map(int, 位置.split(','))
            行 -= 1
            列 -= 1
        except ValueError:
            print('請輸入有效的位置 (例如: 1,3)。')
            continue
        if not 檢查合法性(行, 列, 行數, 列數) or 遊戲地圖[行][列] != '□':
            print('請輸入有效的行數和列數。')
            continue

        if 檢查地雷(地雷地圖, 行, 列):
            生命值 -= 1
            遊戲地圖[行][列] = 'Ｘ'  # 在踩到地雷的位置畫叉
            print(f'踩到地雷！剩餘生命值: {生命值}')
            if 生命值 == 0:
                顯示地雷地圖(地雷地圖)
                print('遊戲結束，你失敗了。')
                break
        else:
            周圍地雷數 = 計算周圍地雷數(地雷地圖, 行, 列)
            遊戲地圖[行][列] = 中文數字[周圍地雷數]
            顯示遊戲地圖(遊戲地圖)

        # 檢查勝利條件
        地雷方塊數 = sum(row.count('□') for row in 遊戲地圖)+sum(row.count('Ｘ') for row in 遊戲地圖)
        if 地雷方塊數 == 地雷數 and 生命值 > 0:
            顯示地雷地圖(地雷地圖)
            print('恭喜你獲勝！')
            break

if __name__ == "__main__":
    主程式()
```

### 3. 幾A幾B：
```python=
import random

def 生成祕密數字():
  數字列表 = [str(i) for i in range(10)]
  random.shuffle(數字列表)
  return ''.join(數字列表[:4])

def 獲取猜測():
  while True:
    猜測 = input("請猜一個四位數字（數字不重複）：")
    if len(猜測) == 4 and 猜測.isdigit() and len(set(猜測)) == 4:
      return 猜測
    print("無效的輸入！")

def 獲取提示(猜測, 祕密數字):
  A的數量 = sum(1 for a, b in zip(猜測, 祕密數字) if a == b)
  B的數量 = sum(1 for g in 猜測 if g in 祕密數字) - A的數量
  return f"{A的數量}A{B的數量}B"

def 遊戲開始():
  祕密數字 = 生成祕密數字()
  嘗試次數 = 0

  while True:
    猜測 = 獲取猜測()
    嘗試次數 += 1
    if 猜測 == 祕密數字:
      print(f"恭喜你猜對了！答案就是 {祕密數字}。你總共猜了 {嘗試次數} 次。")
      break
    提示 = 獲取提示(猜測, 祕密數字)
    print(f"提示：{提示}")

if __name__ == "__main__":
  遊戲開始()
```

## Lecture9：運用Python進行機器學習

[實用的可視化機器學習範例](https://playground.tensorflow.org/#activation=tanh&batchSize=10&dataset=circle&regDataset=reg-plane&learningRate=0.03&regularizationRate=0&noise=0&networkShape=4,2&seed=0.38938&showTestData=false&discretize=false&percTrainData=50&x=true&y=true&xTimesY=false&xSquared=false&ySquared=false&cosX=false&sinX=false&cosY=false&sinY=false&collectStats=false&problem=classification&initZero=false&hideText=false)
可以先試著玩玩看這個，再看下面的程式碼實作範例。

### 1. 甚麼是類神經網路？
類神經網路（Artificial Neural Network，簡稱ANN）是一種受到人類大腦結構啟發的計算模型，用於機器學習和人工智慧領域。ANN的基本結構由多個相互連接的節點（神經元）組成，這些神經元分佈在不同的層次中，包括輸入層、隱藏層和輸出層。

主要的ANN組成部分包括：

1. **神經元（Neurons）：** 是ANN的基本單位，模擬生物神經元的功能。每個神經元接收一到多個輸入，經過權重加權後，通過激活函數計算輸出。

2. **權重（Weights）：** 用來調節輸入在神經元間的影響程度。訓練ANN的過程就是調整這些權重，以使網絡能夠準確地學習和預測。

3. **激活函數（Activation Function）：** 在神經元內部，激活函數決定神經元的輸出。常見的激活函數包括Sigmoid、ReLU（Rectified Linear Unit）等。

4. **層次結構：** ANN通常由輸入層、隱藏層和輸出層組成。輸入層接收外部輸入，輸出層產生模型的輸出，而隱藏層在兩者之間進行信息處理。

5. **前向傳播（Forward Propagation）：** 是ANN中信息傳遞的過程。從輸入層開始，通過權重和激活函數逐層計算，直到獲得輸出。

6. **反向傳播（Backpropagation）：** 是ANN中的訓練過程。通過比較實際輸出和預測輸出之間的誤差，利用梯度下降算法調整權重，以最小化誤差。

ANN被廣泛應用於圖像識別、語音識別、自然語言處理、遊戲AI等領域。深度學習模型，如深度神經網路（Deep Neural Networks，DNN）和卷積神經網路（Convolutional Neural Networks，CNN）是ANN的進階形式，更能處理複雜的任務和大量數據。

### 2. 每一層神經元的權重（係數）和偏置（偏移）是甚麼意思？

在神經網路中，每一層的神經元都與上一層的所有神經元相連，這些連接有相應的權重（weights）和偏置（bias）。這兩者是神經網路中調整和影響信息傳播的重要參數。

1. **權重（Weights）：** 每個連接都有一個權重，用來表示兩個相連神經元之間的連接強度或影響程度。在前向傳播過程中，輸入信號會與權重相乘，這樣的操作表徵了不同輸入在神經元間的相對影響。在訓練過程中，這些權重會被調整，以最小化神經網路的輸出和實際輸出之間的誤差。

2. **偏置（Bias）：** 每個神經元都有一個偏置，它用來調整該神經元的激活閾值。在前向傳播時，每個神經元的輸入總和會加上對應的偏置。偏置的作用是使神經元更容易或更難被激活，影響神經元的輸出。就像權重一樣，偏置也在訓練過程中進行調整，以優化網絡的性能。

簡單來說，權重和偏置是神經網路中用來調整和學習的參數。它們的調整是通過反向傳播算法來實現的，該算法使用梯度下降來最小化預測輸出和實際輸出之間的誤差。透過訓練，神經網路能夠自動調整這些權重和偏置，以更好地擬合給定的任務或問題。

### 3. loss function是甚麼？
損失函數（Loss Function）是在機器學習和深度學習中用來衡量模型預測輸出與實際目標之間差異的函數。它是模型訓練過程中的一個關鍵部分，目標是最小化損失函數的值，從而使模型的預測更接近實際目標。

損失函數通常表示為一個數學函數，接收模型的預測輸出和實際目標，然後輸出一個衡量它們之間差異的值。這個值越小，表示模型的預測越準確。不同的任務和模型可能需要使用不同的損失函數。

以下是一些常見的損失函數：

1. **均方誤差（Mean Squared Error，MSE）：** 用於回歸問題，計算預測值和實際值之間的平方差的平均值。

2. **交叉熵損失（Cross-Entropy Loss）：** 用於分類問題，特別是在神經網路中的分類任務。包括二元交叉熵（Binary Cross-Entropy）和多元交叉熵（Categorical Cross-Entropy）。

3. **Huber Loss：** 介於均方誤差和平均絕對誤差之間，對異常值更具魯棒性。

4. **平均絕對誤差（Mean Absolute Error，MAE）：** 也用於回歸問題，計算預測值和實際值之間的絕對差的平均值。

5. **對比損失（Contrastive Loss）：** 用於學習相似性比較的模型，例如孿生網路。

6. **樞紐損失（Hinge Loss）：** 用於支持向量機（Support Vector Machines）和一些二元分類模型。

在訓練過程中，優化算法會使用梯度下降等方法調整模型的參數（例如權重和偏置），以最小化損失函數的值。這個過程被稱為反向傳播（Backpropagation）。

### 4. epoch是甚麼？
在機器學習和深度學習中，一個「epoch」代表對整個訓練數據集的一次完整遍歷。換句話說，在一個epoch中，模型通過整個訓練數據集的所有樣本進行了一次前向傳播和反向傳播的過程，並通過優化算法（例如梯度下降）更新了模型的參數，以提高對目標的預測能力。

在實際應用中，通常會將訓練數據集分為小批次（mini-batches）進行處理，每次處理一個小批次的數據。這樣的訓練方式有助於加速計算並節省記憶體。因此，一個epoch實際上可能包含多個小批次的訓練過程。

在模型訓練過程中，通常會設定訓練的總epoch數，這是一個超參數。當模型完成指定數量的epoch後，訓練過程就結束了。在實際應用中，通常會根據模型的性能和訓練時間來調整epoch的數量。

總之，一個epoch代表一次完整的訓練過程，其中模型通過整個訓練數據集進行學習。


### 5. 如何用python生成數據點？
```python=
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from sklearn.model_selection import train_test_split

N = 200  # 原始點的數量
Np = 1200  # 額外點的數量

def generate_circle_points(r_min, r_max, num_points):
    angles = np.random.uniform(0, 2 * np.pi, num_points)
    radii = np.sqrt(np.random.uniform(r_min**2, r_max**2, num_points)) # 為什麼要這樣才會均勻, 請問數學老師
    x = radii * np.cos(angles)
    y = radii * np.sin(angles)
    return x, y

# 生成數據點
R0 = 0
R1 = 2
R2 = 1
R3 = 3
x_o, y_o = generate_circle_points(R0, R1, N)
x_x, y_x = generate_circle_points(R2, R3, N)

# 繪製散點圖
plt.figure(figsize=(8, 8))
plt.scatter(x_o, y_o, c='blue', marker='o', label='Positive O')
plt.scatter(x_x, y_x, c='orange', marker='o', label='Negative O')

plt.xlabel('X')
plt.ylabel('Y')
plt.title('Scatter Plot with Different Classes')
plt.legend()
plt.grid(True)
plt.axis('equal')
plt.show()
```
* 範例圖示：
![5](https://hackmd.io/_uploads/BJKN1MwV6.png)

### 6. 把**loss fuction**和**預測模型**加進來，變成完整的程式：
```python=
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from sklearn.model_selection import train_test_split


N = 200  # 原始點的數量
Np = 1200  # 額外點的數量

def generate_circle_points(r_min, r_max, num_points):
    angles = np.random.uniform(0, 2 * np.pi, num_points)
    radii = np.sqrt(np.random.uniform(r_min**2, r_max**2, num_points)) # 為什麼要這樣才會均勻, 請問數學老師
    x = radii * np.cos(angles)
    y = radii * np.sin(angles)
    return x, y

def plot_predictions(X_train, y_train, X_test, y_test, model):
    # 使用模型對訓練集進行預測
    y_pred_train_continuous = model.predict(X_train)
    y_pred_train = np.where(y_pred_train_continuous.flatten() >= 0, 1, -1)

    # 使用模型對測試集進行預測
    y_pred_test_continuous = model.predict(X_test)
    y_pred_test = np.where(y_pred_test_continuous.flatten() >= 0, 1, -1)
    plt.figure(figsize=(8, 8))

    # 訓練集 - 預測正確
    correct_train = (y_train == y_pred_train)
    oc = (y_train == 1) & correct_train
    xc = (y_train == -1) & correct_train
    plt.scatter(X_train[oc, 0], X_train[oc, 1], c='blue', marker='o', label='Train Correct (Positive)')
    plt.scatter(X_train[xc, 0], X_train[xc, 1], c='orange', marker='o', label='Train Correct (Negative)')

    # 訓練集 - 預測錯誤
    incorrect_train = (y_train != y_pred_train)
    oi = (y_train == 1) & incorrect_train
    xi = (y_train == -1) & incorrect_train
    plt.scatter(X_train[oi, 0], X_train[oi, 1], c='blue', marker='x', label='Train Incorrect (Positive)')
    plt.scatter(X_train[xi, 0], X_train[xi, 1], c='orange', marker='x', label='Train Incorrect (Negative)')

    # 測試集 - 預測正確
    correct_test = (y_test == y_pred_test)
    oc = (y_test == 1) & correct_test
    xc = (y_test == -1) & correct_test
    plt.scatter(X_test[oc, 0], X_test[oc, 1], c='cyan', marker='o', label='Test Correct (Positive)')
    plt.scatter(X_test[xc, 0], X_test[xc, 1], c='pink', marker='o', label='Test Correct (Negative)')

    # 測試集 - 預測錯誤
    incorrect_test = (y_test != y_pred_test)
    oi = (y_test == 1) & incorrect_test
    xi = (y_test == -1) & incorrect_test
    plt.scatter(X_test[oi, 0], X_test[oi, 1], c='cyan', marker='x', label='Test Incorrect (Positive)')
    plt.scatter(X_test[xi, 0], X_test[xi, 1], c='pink', marker='x', label='Test Incorrect (Negative)')

    plt.xlabel('X Coordinate')
    plt.ylabel('Y Coordinate')
    plt.title('Model Predictions')
    plt.legend()
    plt.grid(True)
    plt.show()


# 生成數據點
R0 = 0
R1 = 2
R2 = 1
R3 = 4
x_o, y_o = generate_circle_points(R0, R1, N)
x_x, y_x = generate_circle_points(R2, R3, N)

# 整合並標記數據點
X = np.vstack((np.column_stack((x_o, y_o)), np.column_stack((x_x, y_x))))
y = np.array([1] * N + [-1] * N)  # 1代表o，-1代表x

# 分割數據集, 把 N 中的 20% 做為測試
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 創建神經網路模型
N1 = 4
model = Sequential()
model.add(Dense(N1, input_dim=2, activation='relu'))  # 第一層有N1個神經元，使用sigmoid激活函數
model.add(Dense(2, input_dim=2, activation='relu'))  # 第二層有2個神經元
model.add(Dense(1, activation='relu'))  # 第三層有1個神經元

# 編譯模型
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy'])

# 繪製散點圖
plt.figure(figsize=(8, 8))
plt.scatter(x_o, y_o, c='blue', marker='o', label='Positive O')
plt.scatter(x_x, y_x, c='orange', marker='o', label='Negative O')

plt.xlabel('X')
plt.ylabel('Y')
plt.title('Scatter Plot with Different Classes')
plt.legend()
plt.grid(True)
plt.axis('equal')
plt.show()

# 訓練模型並記錄訓練過程
history = model.fit(X_train, y_train, epochs=200, batch_size=5, verbose=0, validation_data=(X_test, y_test))

# 獲取損失函數值
loss_values = history.history['loss']

# 繪製損失函數圖
plt.figure(figsize=(8, 4))
plt.plot(loss_values, label='Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Loss Function During Training')
plt.legend()
plt.grid(True)
plt.show()

plot_predictions(X_train, y_train, X_test, y_test, model)


# 獲取第一層的權重和偏置
weights, biases = model.layers[0].get_weights()

# 生成更多隨機點進行預測
x_a, y_a = generate_circle_points(R0, R3, Np)
additional_X = np.column_stack((x_a, y_a))
y_pred_additional = model.predict(additional_X)
y_pred_additional = np.where(y_pred_additional.flatten() >= 0, 1, -1)

# 繪製測試數據和預測結果
plt.figure(figsize=(8, 8))
plt.scatter(x_o, y_o, c='blue', marker='o', label='Class Positive')
plt.scatter(x_x, y_x, c='orange', marker='o', label='Class Negative')
plt.scatter(additional_X[y_pred_additional == 1, 0], additional_X[y_pred_additional == 1, 1], c='blue', marker='o', label='Predicted Positive')
plt.scatter(additional_X[y_pred_additional == -1, 0], additional_X[y_pred_additional == -1, 1], c='orange', marker='o', label='Predicted Negative')

# 繪製第一層的N1條直線
x_values = np.linspace(-2, 2, 100)
y_values = np.linspace(-2, 2, 100)
for i in range(N1):
    a, b = weights[:, i]
    c = biases[i]
    if abs(a) > abs(b):
        x_line = (-b * y_values - c) / a
        plt.plot(x_line, y_values, linestyle='--', label=f'Neuron {i+1} Boundary')
    else:
        y_line = (-a * x_values - c) / b
        plt.plot(x_values, y_line, linestyle='--', label=f'Neuron {i+1} Boundary')

plt.xlabel('X')
plt.ylabel('Y')
plt.title('Additional Points Classification with Neuron Boundaries')
plt.legend()
plt.grid(True)
plt.axis('equal')
plt.show()
```
* 訓練結果：
![6](https://hackmd.io/_uploads/rJaklMwEa.png)
![7](https://hackmd.io/_uploads/SJixgzPNp.png)
![8](https://hackmd.io/_uploads/SJoggfvVa.png)
![9](https://hackmd.io/_uploads/rJjlgfDE6.png)

## Reference

[ChatGPT Python程式設計協助](https://chat.openai.com/share/3c55b863-b57d-41ab-afe4-db6c45610339)

國立成功大學「Python程式設計入門」上課內容 舒宇宸教授


















