深入探索Mamba模型架構與應用 - 商品搜索 - 京東
?DeepSeek大模型高性能核心技術與多模態融合開發 - 商品搜索 - 京東
由于大氣運動極為復雜,影響天氣的因素較多,而人們認識大氣本身運動的能力極為有限,因此以前天氣預報水平較低?。預報員在預報實踐中,每次預報的過程都極為復雜,需要綜合分析,并預報各氣象要素,比如溫度、降水等。現階段,以往極少出現的極端天氣現象越來越多,這極大地增加了預報的難度,如圖11-20所示。
圖11-20? 天氣預報
本小節將使用Jamba完成天氣預測,即在給定天氣因素下,預測城市是否下雨。
1. 數據集的準備
這里作者準備了來自澳大利亞多個氣候站的日常共15萬條收集[wy1]?[曉王2]?的數據,如圖11-21所示。
圖11-21? 澳大利亞天氣數據集前5條內容
其中的變量解釋如下所示。
- Location?? ?獲取該信息的氣象站的名稱
- MinTemp?? ?以攝氏度為單位的低溫度
- MaxTemp?? ?以攝氏度為單位的高溫度
- Rainfall?? ?當天記錄的降雨量,單位為mm
- Evaporation?? ?到早上9點之前的24小時的A級蒸發量(mm)
- Sunshine?? ?白日受到日照的完整小時
- WindGustDir?? ?在到午夜12點前的24小時中的強風的風向
- WindGustSpeed?? ?在到午夜12點前的24小時中的強風速(km/h)
- WindDir9am?? ?上午9點時的風向
- WindDir3pm?? ?下午3點時的風向
- WindSpeed9am?? ?上午9點之前每10分鐘的風速的平均值(km/h)
- WindSpeed3pm?? ?下午3點之前每10分鐘的風速的平均值(km/h)
- Humidity9am?? ?上午9點的濕度(百分比)
- Humidity3am?? ?下午3點的濕度(百分比)
- Pressure9am?? ?上午9點平均海平面上的大氣壓(hpa)
- Pressure3pm?? ?下午3點平均海平面上的大氣壓(hpa)
- Cloud9am?? ?上午9點的天空被云層遮蔽的程度,這是以oktas來衡量的,這個單位記錄了云層遮擋天空的程度。0表示完全晴朗的天空,而8表示完全是陰天
- Cloud3pm?? ?下午3點的天空被云層遮蔽的程度
- Temp9am?? ?上午9點的攝氏度溫度
- Temp3pm?? ?下午3點的攝氏度溫度
- RainToday?? ?今日是否有雨
- RainTomorrow?? ?明日是否有雨
通過觀察可以發現,這個特征矩陣由一部分分類變量和一部分連續變量組成,其中云層遮蔽程度雖然是以數字表示的,但是其本質卻是分類變量。大多數特征都是采集的自然數據,比如蒸發量、日照時間、濕度等,而少部分特征則是人為構成的。還有一些是單純表示樣本信息的變量,比如采集信息的地點,以及采集的時間。
在我們知道了數據情況后,下一步就是確定目標標簽,簡單來說,就是我們的標簽:明天下雨嗎?目標特征RainTomorrow表示明天是否會下雨,即是或否。
2. 特征的選擇與程序實現
在特征選擇過程中,我們需要根據目標精準地挑選最合適的特征進行計算。以天氣為例,我們推測昨天的天氣狀況可能會對今天的天氣狀況有所影響,同樣,今天的天氣狀況也可能影響明天的天氣狀況。換言之,在樣本之間,隨著時間的推移,是存在相互影響的。然而,傳統的算法往往只能捕捉到樣本特征與標簽之間的關系,即列與列之間的聯系,而無法把握樣本與樣本之間的關聯,也就是行與行之間的聯系。
要讓算法能夠理解前一個樣本標簽對后一個樣本標簽的潛在影響,我們需要引入時間序列分析。時間序列分析是通過將同一統計指標的數據按其發生的時間順序排列,以數列形式呈現出來。其主要目的是依據歷史數據預測未來趨勢。但據作者了解,時間序列分析通常適用于單調且唯一的時間線,即它一次只能預測一個地點的數據,而無法同時預測多個地點,除非進行循環操作。然而,我們的時間數據并非單調或唯一的,尤其是在抽樣后,數據的連續性也可能被破壞。我們的數據混雜了多個地點和不同時間段的信息,這使得使用時間序列分析來處理問題變得相當復雜。
那么,我們可以嘗試另一種思路。既然算法擅長處理的是列與列之間的關系,我們是否可以將“今天的天氣影響明天的天氣”這一因素轉換為一個具體的特征呢?實際上,這是可行的。
我們注意到數據中有一個特征列名為RainToday,表示當前日期和地區的降雨量,即“今日的降雨量”。基于常識,我們認為今天是否下雨很可能會影響明天的天氣狀況。例如,在某些氣候區域,一旦開始下雨,可能會持續數日;而在另一些地方,暴雨可能來得快,去得也快。因此,我們可以將時間對氣候的連續影響轉換為“今天是否下雨”這一特征。這樣,我們可以巧妙地將原本樣本間(行與行之間)的聯系轉換為特征與標簽之間的聯系(列與列之間)。
下面將使用其中6個特征'Location'、'WindGustDir'、'WindDir9am'、'WindDir3pm'、'Date'和'RainToday'完成對明日天氣的預測。代碼如下:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as npdef preprocessing(df, index):labels = set([])for label in df[index]:labels.add(label)labels = list(labels)# print(labels)column = df[index]df.drop(axis=1, columns=index, inplace=True)if 'RainT' in index:temp = column.map(lambda x: labels.index(x))elif index == 'Date':temp = column.map(lambda x: x[5] if x[6] == '/' else x[5:7])else:temp = column.map(lambda x: labels.index(x) + 1)df.insert(0, index, temp)return dfdef get_dataset(location): # 獲取數據集df = pd.read_csv(location)# df = df[['Location', 'MinTemp', 'MaxTemp', 'Rainfall', 'Evaporation', 'Sunshine', 'WindGustDir', 'WindGustSpeed',# 'WindSpeed9am', 'WindSpeed3pm', 'Humidity9am', 'Humidity3pm', 'Pressure9am', 'Pressure3pm',# 'Cloud9am', 'Cloud3pm', 'Temp9am', 'Temp3pm', 'RainToday', 'RainTomorrow']]df = df.dropna() # 去除包含缺失值的行# 用整數標簽代替字符串型數據for label in ['Location', 'WindGustDir', 'WindDir9am', 'WindDir3pm', 'Date', 'RainToday', 'RainTomorrow']:df = preprocessing(df, label)# Date列格式轉換date = df['Date'].astype(float)df.drop(axis=1, columns='Date', inplace=True)df.insert(2, 'Date', date)# 刪除相關系數過低的數據for key in df.corr()['RainTomorrow'].keys():if (df.corr()['RainTomorrow'][key] < 0.1) & (df.corr()['RainTomorrow'][key] > -0.1):df.drop(axis=1, columns=key, inplace=True)return dfdf = get_dataset('./weatherAUS.csv')
df_list = df.values.tolist()
feature = df.drop(['RainTomorrow'], axis=1).values.tolist()
label = df['RainTomorrow'].values.tolist()feature = np.array(feature)
label = np.array(label)
可以看到,我們使用Pandas抽取了6個特征作為訓練特征,而RainTomorrow作為預測值被記錄。數據量的多少讀者可以打印完成。
下面將生成數據整理成PyTorch數據讀取格式為模型訓練做準備,代碼如下:
import torch
from torch.utils.data import Dataset, random_splitclass TextSamplerDataset(torch.utils.data.Dataset):def _ _init_ _(self, feature = feature, label = label):super()._ _init_ _()self.feature = torch.from_numpy(feature)/4.self.label = torch.from_numpy(label).int()def _ _getitem_ _(self, index):return self.feature[index],self.label[index]def _ _len_ _(self):return len(self.label)dataset = TextSamplerDataset(feature,label)# 定義分割比例
train_ratio = 0.9
val_ratio = 0.1# 計算每個分割的數據量
total_size = len(dataset)
train_size = int(train_ratio * total_size)
val_size = int(val_ratio * total_size)# 進行數據分割
train_dataset, val_dataset = random_split(dataset, [train_size, val_size])
在這里,TextSamplerDataset作為數據的文本讀取類,對結果進行整合,并且由于特征值普遍較大,因此在計算時我們采用除以4的方法對其進行修正。random_split的作用是將完整的數據集按9:1的比例劃分為訓練集與測試集。
3. 天氣預測模型的實現
對于天氣預測模型的實現,我們將使用Jamba作為計算核心完成預測模型,而在輸入的部分,由于輸入的是一個1D數據,首先需要對其維度進行調整,整理成一個新的2D向量,從而進行后續的計算。
而輸出層我們采用一個全連接層,將輸入向量計算后整理成一個二分類,從而對結果進行預測。完整的天氣預報模型如下:
class PredicateWeather(torch.nn.Module):def _ _init_ _(self,jamba_dim = 384,jamba_head = 6,feature_num = 13):super()._ _init_ _()self.feature_num = feature_numself.linear = torch.nn.Linear(feature_num,feature_num * jamba_dim)self.jamba = model.Jamba(dim=jamba_dim,attention_head_num=jamba_head)self.logits_layer = torch.nn.Linear(feature_num * jamba_dim,2)def forward(self,x):x = self.linear(x)x = einops.rearrange(x,"b (f d) -> b f d",f = self.feature_num)x = self.jamba(x)x = torch.nn.Flatten()(x)x = torch.nn.Dropout(0.1)(x)logits = self.logits_layer(x)return logits
4. 天氣預報模型的訓練與驗證
最后,我們將使用模型在澳大利亞天氣數據集上完成預測任務。在這里,我們可以同時對損失值和正確率進行監測,并通過tqdm類具體實現,代碼如下:
import torch
from tqdm import tqdm
import weather_predicate
import get_weather_datasetfrom torch.utils.data import DataLoadermodel = weather_predicate.PredicateWeather()batch_size = 256
device = "cuda"model = model.to(device)# 創建數據加載器
train_loader = DataLoader(get_weather_dataset.train_dataset, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(get_weather_dataset.val_dataset, batch_size=batch_size, shuffle=False)import torch
optimizer = torch.optim.AdamW(model.parameters(), lr = 2e-4)
lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer,T_max = 6000,eta_min=2e-6,last_epoch=-1)
criterion = torch.nn.CrossEntropyLoss()for epoch in range(24):correct = 0 # 用于記錄正確預測的樣本數量total = 0 # 用于記錄總樣本數量pbar = tqdm(train_loader,total=len(train_loader))for feature,label in pbar:optimizer.zero_grad()feature = feature.float().to(device)label = label.long().to(device)logits = model(feature)loss = criterion(logits.view(-1, logits.size(-1)), label.view(-1))loss.backward()optimizer.step()lr_scheduler.step() # 執行優化器# 計算正確率_, predicted = torch.max(logits.data, 1) # 得到預測結果total += label.size(0) # 更新總樣本數量correct += (predicted == label).sum().item() # 更新正確預測的樣本數量accuracy = 100 * correct / total # 計算正確率pbar.set_description(f"epoch:{epoch +1},accuracy:{accuracy:.3f}%, train_loss:{loss.item():.5f}, lr:{lr_scheduler.get_last_lr()[0]*1000:.5f}")# 測試模型
model.eval()
test_correct = 0 # 用于記錄正確預測的樣本數量
test_total = 0 # 用于記錄總樣本數量
with torch.no_grad():for feature, label in val_loader:feature = feature.float().to(device)label = label.long().to(device)logits = model(feature)# 計算正確率_, predicted = torch.max(logits.data, 1) # 得到預測結果test_total += label.size(0) # 更新總樣本數量test_correct += (predicted == label).sum().item()# 更新正確預測的樣本數量test_accuracy = 100 * test_correct / test_total # 計算正確率print(f"在測試集上的準確率為:{test_accuracy}")
在這里,我們同時完成了模型的訓練以及對結果的預測,并且在劃分的驗證集上獲得了對結果準確率的驗證,如下所示:
epoch:1,accuracy:69.912%, train_loss:3.56852 100%|██████████| 199/199 [00:26<00:00, 7.60it/s]
epoch:2,accuracy:74.985%, train_loss:3.15288 100%|██████████| 199/199 [00:26<00:00, 7.64it/s]
...
epoch:24,accuracy:82.689%, train_loss:0.4148 100%|██████████| 199/199 [00:26<00:00, 7.48it/s]在測試集上的準確率為:84.36724565756823
可以看到,經過24輪的預測,在訓練集上我們獲得了82.69%的準確率,而將模型和訓練參數遷移到驗證集上,我們獲得了84.37%的準確率。當然,從訓練集上的結果來看,此時可能還沒有充分完成特征的訓練,模型仍舊除于“欠擬合”狀態,還需要繼續訓練,這一點有興趣的讀者可以繼續嘗試完成。