Bucket有點像電腦里面的盤符或者目錄,我們文件的上傳,必須指定上傳到哪個Bucket里面。因此,在上傳之前必須創建它。
在阿里云控制臺點擊OSS服務,然后點擊【Bucket列表】就可以看到如下界面:
然后點擊【創建Bucket】按鈕,按下圖填寫:
Bucket名稱:需要自己要進行命名,相當于自己文件的存儲目錄。
區域:這個選擇很重要,因為后面調用api接口上傳文件的時候需要使用。
Endpoint:這個也很重要,因為后面調用api接口上傳文件的時候也需要使用。
上述選擇完成后,就可以點擊【確定】按鈕進行創建了。
創建成功后,進入Bucket信息頁面,可以點擊左側的【文件管理】按鈕,出現如下界面:
好了,到此為止,阿里云的OSS配置就算完成了,新手對于這個過程感覺十分困惑,因此我這里說的比較詳細。
下面是官網文檔地址以及官方代碼,我摘抄了幾個入門級的代碼。
https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.11186623.6.787.101245dcJzaFge
(1)上傳字符串
// Endpoint以杭州為例,其它Region請按實際情況填寫。
String endpoint="http://oss-cn-hangzhou.aliyuncs.com";
// 阿里云主賬號AccessKey擁有所有API的訪問權限,風險很高。強烈建議您創建并使用RAM賬號進行API訪問或日常運維,請登錄 https://ram.console.aliyun.com 創建RAM賬號。
String accessKeyId="<yourAccessKeyId>";
String accessKeySecret="<yourAccessKeySecret>";
// 創建OSSClient實例。
OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 創建PutObjectRequest對象。
String content="Hello OSS";
// <yourObjectName>表示上傳文件到OSS時需要指定包含文件后綴在內的完整路徑,例如abc/efg/123.jpg。
PutObjectRequest putObjectRequest=new PutObjectRequest("<yourBucketName>", "<yourObjectName>", new ByteArrayInputStream(content.getBytes()));
// 如果需要上傳時設置存儲類型與訪問權限,請參考以下示例代碼。
// ObjectMetadata metadata=new ObjectMetadata();
// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
// metadata.setObjectAcl(CannedAccessControlList.Private);
// putObjectRequest.setMetadata(metadata);
// 上傳字符串。
ossClient.putObject(putObjectRequest);
// 關閉OSSClient。
ossClient.shutdown();
(2)上傳Byte數組
// Endpoint以杭州為例,其它Region請按實際情況填寫。
String endpoint="http://oss-cn-hangzhou.aliyuncs.com";
// 阿里云主賬號AccessKey擁有所有API的訪問權限,風險很高。強烈建議您創建并使用RAM賬號進行API訪問或日常運維,請登錄 https://ram.console.aliyun.com 創建RAM賬號。
String accessKeyId="<yourAccessKeyId>";
String accessKeySecret="<yourAccessKeySecret>";
// 創建OSSClient實例。
OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);
// 上傳Byte數組。
byte[] content="Hello OSS".getBytes();
ossClient.putObject("<yourBucketName>", "<yourObjectName>", new ByteArrayInputStream(content));
// 關閉OSSClient。
ossClient.shutdown();
(3)上傳網絡流
// Endpoint以杭州為例,其它Region請按實際情況填寫。
String endpoint="http://oss-cn-hangzhou.aliyuncs.com";
// 阿里云主賬號AccessKey擁有所有API的訪問權限,風險很高。強烈建議您創建并使用RAM賬號進行API訪問或日常運維,請登錄 https://ram.console.aliyun.com 創建RAM賬號。
String accessKeyId="<yourAccessKeyId>";
String accessKeySecret="<yourAccessKeySecret>";
// 創建OSSClient實例。
OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 上傳網絡流。
InputStream inputStream=new URL("https://www.aliyun.com/").openStream();
ossClient.putObject("<yourBucketName>", "<yourObjectName>", inputStream);
// 關閉OSSClient。
ossClient.shutdown();
(4)上傳文件流
// Endpoint以杭州為例,其它Region請按實際情況填寫。
String endpoint="http://oss-cn-hangzhou.aliyuncs.com";
// 云賬號AccessKey有所有API訪問權限,建議遵循阿里云安全最佳實踐,創建并使用RAM子賬號進行API訪問或日常運維,請登錄 https://ram.console.aliyun.com 創建。
String accessKeyId="<yourAccessKeyId>";
String accessKeySecret="<yourAccessKeySecret>";
// 創建OSSClient實例。
OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 上傳文件流。
InputStream inputStream=new FileInputStream("<yourlocalFile>");
ossClient.putObject("<yourBucketName>", "<yourObjectName>", inputStream);
// 關閉OSSClient。
ossClient.shutdown();
// Endpoint以杭州為例,其它Region請按實際情況填寫。
String endpoint="http://oss-cn-hangzhou.aliyuncs.com";
// 阿里云主賬號AccessKey擁有所有API的訪問權限,風險很高。強烈建議您創建并使用RAM賬號進行API訪問或日常運維,請登錄 https://ram.console.aliyun.com 創建RAM賬號。
String accessKeyId="<yourAccessKeyId>";
String accessKeySecret="<yourAccessKeySecret>";
// 創建OSSClient實例。
OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 創建PutObjectRequest對象。
PutObjectRequest putObjectRequest=new PutObjectRequest("<yourBucketName>", "<yourObjectName>", new File("<yourLocalFile>"));
// 如果需要上傳時設置存儲類型與訪問權限,請參考以下示例代碼。
// ObjectMetadata metadata=new ObjectMetadata();
// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
// metadata.setObjectAcl(CannedAccessControlList.Private);
// putObjectRequest.setMetadata(metadata);
// 上傳文件。
ossClient.putObject(putObjectRequest);
// 關閉OSSClient。
ossClient.shutdown();
首先我們來定一個阿里云OSS文件上傳的一個工具類,具體代碼如下:
package com.shenmazong.utils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectResult;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.UUID;
public class UploadFileUtils {
/**
* TODO uploadAliyunOss 阿里云OSS文件上傳
* @param file
* @param fileName
* @return
*/
public static String uploadAliyunOss(MultipartFile file, String fileName) {
String url=null;
// Endpoint以杭州為例,其它Region請按實際情況填寫。
String endpoint="http://oss-cn-beijing.aliyuncs.com";
// 阿里云主賬號AccessKey擁有所有API的訪問權限,風險很高。強烈建議您創建并使用RAM賬號進行API訪問或日常運維,請登錄 https://ram.console.aliyun.com 創建RAM賬號。
// shenmazong@1642590691341298.onaliyun.com
String accessKeyId="XXXX";
String accessKeySecret="XXXX";
// 創建OSSClient實例。
OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 創建PutObjectRequest對象。
// 上傳文件流。
PutObjectResult result=null;
String newName=UUID.randomUUID().toString();
try {
result=ossClient.putObject("shenmazong", fileName, file.getInputStream());
url="https://shenmazong.oss-cn-beijing.aliyuncs.com/" + fileName;
} catch (IOException e) {
e.printStackTrace();
return null;
}
// 關閉OSSClient。
ossClient.shutdown();
return url;
}
}
上面的工具類,是傳輸web上傳的文件流,然后返回能夠訪問的url,這樣我們web網上實現圖片文件上傳,就很容易了。
下面我們就可以編寫service來實現我們的文件上傳接口了。
/**
* TODO 實現用戶頭像的上傳
* @param userId
* @param file
* @return
*/
@Override
public ResponseResult uploadHead(Integer userId, MultipartFile file) {
ResponseResult result=ResponseResult.SUCCESS();
//--1 驗證用戶是否存在
TbUser user=iTbUserMapper.selectById(userId);
if(user==null) {
result.setCode(-1);
result.setMessage("獲取用戶信息失敗");
return result;
}
//--2 上傳文件
if(file.isEmpty()){
log.info("上傳文件為空");
result.setCode(-1);
result.setMessage("上傳文件為空");
return result;
}
String fileName=file.getOriginalFilename();
int size=(int) file.getSize();
log.info(fileName + "-->" + size);
UUID uuid=UUID.randomUUID();
String suffix=fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
String newName=uuid.toString() + suffix;
String headUrl=UploadFileUtils.uploadAliyunOss(file, newName);
if(headUrl==null) {
result.setCode(-1);
result.setMessage("上傳文件到OSS失敗");
return result;
}
//--3 修改用戶頭像鏈接
user.setHeadurl(headUrl);
iTbUserMapper.updateById(user);
return result;
}
service寫好以后,就需要編寫controller了,具體代碼如下:
/**
* TODO uploadHead 上傳頭像圖片
* @param userId
* @param file
* @return
*/
@ApiOperation(value="頭像上傳", notes="用戶上傳更新自己的頭像")
@ApiImplicitParams(value={
@ApiImplicitParam(name="userId", value="用戶ID", required=true, dataType="int", example="0")
})
@PostMapping(value="/uploadHead", headers="content-type=multipart/form-data")
public ResponseResult uploadHead(@RequestParam("userId") Integer userId,
@RequestParam("uploadFile") MultipartFile file) {
return userService.uploadHead(userId, file);
}
最后是html頁面的示例代碼,也一并貼上來:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- 可選的 Bootstrap 主題文件(一般不用引入) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<div class="row">
<form action="upload" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="uploadFile">選擇文件</label>
<input type="file" id="uploadFile" name="uploadFile">
<p class="help-block">Example block-level help text here.</p>
</div>
<button type="submit" class="btn btn-primary">上傳</button>
</form>
</div>
</div>
</body>
</html>
最后的實例演示,是我在一個演示項目中拷貝過來的代碼,沒有針對咱們這個教程進行整理,但是作為參考資料,這部分代碼是合格的。
018年初,一個物理專業的學生Jan B?hmer創建了一個網站,用來跟蹤和記錄用戶的點擊、鼠標移動、瀏覽器類型和操作系統等數據。雖然用戶跟蹤并不新鮮,但他的方法不需要JavaScript、插件或外部庫。實際上,它只使用了普通HTML文本和一點CSS。
它是如何工作的
B?hmer的概念利用了CSS的兩個特性:將內容注入HTML元素的能力,以及在用戶執行操作后更改樣式的能力。網站的工作方式是在執行操作時使用content屬性設置URL。此URL調用一個腳本,該腳本記錄有關操作的詳細信息,這些操作作為URL參數進行傳遞。使用::before和::after CSS選擇器設置這個URL可以確保只在執行操作時調用URL,而不是在頁面首次加載時調用URL。
例如,下面的CSS在每次單擊#link元素時調用一次URL:
跟蹤腳本包含記錄事件和操作執行次數的代碼。它還可以用于提取用戶的IP地址、用戶代理和其他標識信息。
下面是這樣一個腳本在PHP中的示例:
檢測瀏覽器類型
用戶可以欺騙瀏覽器的用戶代理,但是 B?hmer繞過了這個問題,他使用@supports at-rule(at-rule 是CSS樣式聲明,以@開頭,緊跟著是標識符(charset),最后以分號(;)結尾。)測試瀏覽器特定的CSS屬性。例如,下面的操作通過檢測--webkit-appearance是可用的,而-ms-ime-align是不可用的來檢測Chrome瀏覽器:
檢測操作系統
B?hmer甚至使用字體檢測來識別用戶的操作系統。例如,通過檢測瀏覽器是否支持Calibri字體家族,我們可以假定瀏覽器運行在Windows中:
B?hmer關于此概念的驗證可以識別其他數據點,包括瀏覽器窗口的大小和方向、用戶是否單擊了鏈接以及用戶在一個元素上停留的時間。
這種攻擊在瀏覽器中非常難以預防。完全防止它的唯一方法就是禁用CSS,這會使網站無法使用。然而,通過使用內容安全策略(CSP),可以減少攻擊者利用此漏洞的機會。
使用內容安全策略減少CSS泄漏
CSP是一組規則,它決定瀏覽器可以執行哪些操作,不能執行哪些操作。CSP通常用于防止跨站腳本攻擊(XSS)和由瀏覽器加載不信任腳本導致的其他攻擊。雖然CSP通常用于JavaScript文件,但它也可以應用于CSS樣式和樣式表。
考慮一個使用第三方提供商托管的樣式表的網站。攻擊者破壞樣式表并將用戶跟蹤代碼添加到頁面上的鏈接:
當用戶點擊該鏈接時,他們的瀏覽器調用evil.com上托管的跟蹤腳本。由于這完全是通過瀏覽器完成的,網站所有者完全不知道這個漏洞。
Content-Security-Policy通過設置允許哪些樣式以及樣式來源等規則來防止這種情況。
禁用內聯樣式
禁用內聯樣式是CSP提供的最大安全好處之一。內聯樣式是直接在HTML文檔中聲明的樣式(或使用JavaScript設置的樣式),而不是從樣式表加載的樣式。內聯樣式——尤其是動態生成的樣式或用戶創建的樣式——非常難以保護。這就是為什么CSP通常會鎖定所有內聯腳本和樣式,并將那些已被特別批準的內聯腳本和樣式列入白名單。
以下規則將阻止所有內聯樣式以及外部托管的樣式表:
使用Hash和Nonce驗證樣式
如果阻塞內聯樣式是不可行的,你仍然可以使用hash和nonce來確保CSS的完整性。
Hash是由一個文件或字符串的內容生成的單向字符串。在樣式表或內聯樣式上執行哈希函數時,除非樣式發生改變,否則它總是返回相同的結果。這對于將某些內聯樣式和樣式表加入白名單是很有用的,只需要同時驗證樣式沒有被修改或篡改。
Nonce的功能與hash類似。使用nonce,將為每個請求生成一個新的隨機數,這使得攻擊者更難猜測它的值。這避免了hash的一個關鍵缺點,即多個輸入可能生成相同的hash值(稱為沖突)。
驗證外部托管的樣式表
樣式表通常托管在第三方服務器上,如內容交付網絡(content delivery networks, CDNs),但這帶來了新的攻擊方向。如果CDN受到威脅,如何阻止攻擊者用自己修改過的版本替換樣式表?子資源完整性,也叫SRI,試圖解決這個問題。
SRI使用hash值來驗證腳本和樣式表的內容。計算每個文件的hash值,并將其附加到HTML元素的integrity屬性中。當瀏覽器下載腳本或樣式表時,計算其hash值并將其與存儲在屬性中的值進行比較。如果匹配,瀏覽器將加載腳本或樣式。
這是在假設web頁面是從受信任的源(如源服務器)提交的情況下運行的,而當資源是從不受信任的源(如第三方)提交的時候,就無法正常運行。如果web頁面和資源都由第三方托管,攻擊者只需要簡單地修改web頁面來匹配其CSS替換文件的hash值即可。
結論
雖然通過CSS跟蹤用戶的能力并不新鮮,但它確實要求我們以不同的方式考慮網頁上的隱私和安全性。CSS是現代網頁的基本語言之一,禁用網站的CSS將使網頁的大部分內容無法使用。內容安全策略是阻止XSS攻擊和CSS泄漏的最佳方法。Templarbit創建了一個“靈活的內容-安全-策略工作流”,以便于維護CSP頭文件。如果你的團隊正在努力為你的應用程序推出CSP,請立即注冊一個免費試用版,并學習更多關于Templarbit如何解決CSS泄露的方法.
你可以在GitHub上找到B?hmer的概念驗證的源代碼。
相關連接:
網站——http://crookedss.bplaced.net/
最大的安全好處——https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src#Unsafe_inline_styles
通常鎖定所有內聯腳本——https://developers.google.com/web/fundamentals/security/csp/
加入白名單——https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src
并不新鮮——https://www.smashingmagazine.com/2014/10/css-only-solution-for-ui-tracking/
現在就注冊獲取免費版——https://www.templarbit.com/signup
GitHub——https://github.com/jbtronics/CrookedStyleSheets
相關知識:
SRI,Subresource Integrity 的縮寫,中文:子資源完整性,由 Web 應用安全工作組(Web Application Security Working Group)發布。
英文原文:https://www.templarbit.com/blog/2018/03/20/tracking-users-with-css/
譯者:憂郁的紅秋褲
互聯網上有很多資源可以找到關于機器學習數據集的見解和訓練模型,但是關于如何使用這些模型構建實際應用程序的文章很少。
因此,今天我們將通過首先使用hackathon中的數據集來訓練視頻游戲銷售預測模型,然后使用經過訓練的模型來創建一個基本應用程序,根據用戶輸入為我們提供銷售預測來學習此過程。
本文分為多個部分,你可以一個接一個地學習,而不必一口氣完成它。從我第一次選擇數據集以來,我花了整整一周的時間來完成應用程序。因此,請花時間專注于學習構建應用程序的各個方面,而不是只注意最終產品。
我們將使用在Machine Hack網站上運行的視頻游戲銷售預測hackathon中的數據集。首先,在MachineHack上創建一個帳戶,然后在此鏈接上注冊hackathon。
注冊后,轉到數據標簽并下載zip文件,該文件將包含三個文件,即訓練,測試和樣品提交。
下一步將在Google Colab Notebook中介紹,你可以從以下鏈接打開和克隆該Notebook:
或者如果你想在本地或其他平臺上下載并運行該Notebook,請從以下GitHub鏈接下載該Notebook:Jupyter Notebook鏈接
Notebook電腦的第一部分簡要介紹了問題陳述。通過運行下面顯示的下一個代碼單元,上傳我們收到的文件
from google.colab import files
uploaded=files.upload()
for fn in uploaded.keys():
print('User uploaded file "{name}" with length {length} bytes'.format(
name=fn, length=len(uploaded[fn])))
在下一個代碼單元中,我們導入所需的python包。它們中的大多數已預先安裝在Google Colab中,因此無需安裝它們中的任何一個。
因為我們無法在hackathon結束后提交測試數據進行評估,因此本文其余部分僅將數據用于Train.csv。請記住,Train.csv的行數少于通常用于正確訓練模型的行數。但是,出于學習目的,我們可以使用行數較少的數據集。
現在讓我們深入研究解決此機器學習問題…
步驟1:識別目標和獨立特征
首先,讓我們將Train.csv導入pandas數據框中,然后運行df.head()以查看數據集中的列。
從數據框中,我們可以看到目標列是SalesInMillions,其余列是獨立特征
步驟2:清理資料集
首先,我們通過運行input.isnull().sum()命令檢查null值。
input.isnull().sum()
#Output:
ID 0
CONSOLE 0
YEAR 0
CATEGORY 0
PUBLISHER 0
RATING 0
CRITICS_POINTS 0
USER_POINTS 0
SalesInMillions 0
dtype: int64
我們可以看到數據集中沒有空值。接下來,我們可以通過運行以下命令刪除不必要的列ID,因為它在目標銷售中不起作用:input=input.drop(columns=['ID'])
接下來,我們可以使用train_test_split命令將數據框分為訓練和測試數據集:
train, test=train_test_split(input, test_size=0.2, random_state=42, shuffle=True)
步驟3:探索性數據分析
描述性統計信息
使用df.shape命令我們可以找到數據集中的總行數,并且可以使用命令df.nunique()在每個列中查找唯一值。
CONSOLE 17
YEAR 23
CATEGORY 12
PUBLISHER 184
RATING 6
CRITICS_POINTS 1499
USER_POINTS 1877
SalesInMillions 2804
在EDA部分中,我們使用pandas profiling和matplotlib包生成各種列的圖形,并觀察它們與目標列的關系。
從EDA獲得的一些見解是:
PS3平臺的銷售額最高。之后是Xbox360:
動作類別的銷售額最高,難題類別的銷售額最低
2007年到2011年的銷售額最高:
通常,我們在EDA之后進行特征工程或特征選擇步驟。但是,我們的功能較少,著重于實際使用模型。因此,我們正在朝著下一步邁進。但是,請記住,USER_POINTS和CRITICS_POINTS列可用于派生其他功能。
步驟4:建立模型
由于我們具有許多分類功能,因此我們將對數據集使用catboost回歸模型。由于catboost可以直接作用于分類特征,因此跳過了對分類特征進行標簽編碼的步驟。
首先,我們使用pip install命令安裝catboost軟件包。
然后,我們創建一個分類特征列表,將其傳遞給模型,然后將模型擬合到訓練數據集上:
import catboost as cat
cat_feat=['CONSOLE','CATEGORY', 'PUBLISHER', 'RATING']
features=list(set(train.columns)-set(['SalesInMillions']))
target='SalesInMillions'
model=cat.CatBoostRegressor(random_state=100,cat_features=cat_feat,verbose=0)
model.fit(train[features],train[target])
步驟5:檢查模型的準確性
首先,我們根據測試數據集創建真實的預測:
y_true=pd.DataFrame(data=test[target], columns=['SalesInMillions'])
test_temp=test.drop(columns=[target])
接下來,我們在測試數據集上運行訓練良好的模型以獲取模型預測并檢查模型準確性
y_pred=model.predict(test_temp[features])
from sklearn.metrics import mean_squared_error
from math import sqrt
rmse=sqrt(mean_squared_error(y_true, y_pred))
print(rmse)
#Output: 1.5555409360901584
我們的RMSE值為1.5,這相當不錯。有關在出現回歸問題時準確性指標的更多信息,可以參考本文。
如果你想進一步改善模型或嘗試組合各種模型,可以在本文中參考本次hackathon獲勝者的方法:
步驟6:將模型保存到pickle文件中
現在,我們可以將模型保存到pickle文件中,然后將其保存在本地:
import pickle
filename='finalized_model.sav'
pickle.dump(model, open(filename, 'wb'))
保存pickle文件后,你可以從Google Colab Notebook文件部分的左側邊欄中下載并保存在本地
額外提示
我們可以通過向模型添加更多數據來改善模型預測。我們可以使用一些在Kaggle上的相關的數據集。Kaggle:https://www.kaggle.com/gregorut/videogamesales
我們可以使用組合模型的堆棧來進一步提高模型效率。
如果你已完成此步驟,請輕拍一下自己的背,因為我們剛剛完成了項目的第一個主要部分。休息一會兒,拉伸一下,然后開始本文的下一部分。
我們將使用Python Flask創建后端API。
因此,首先在本地創建一個名為server的文件夾。另外,如果還沒有,請在你的計算機上安裝Python和pip軟件包管理器。
接下來,我們需要在文件夾中創建一個虛擬環境。我在Linux上使用python3,因此我創建虛擬環境的命令為:python3 -m venv server。
你可以在本文中查看適用于你的OS和Python版本的命令:Python venv:https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/
接下來,我們將通過運行以下命令激活虛擬環境: source server/bin/activate
完成后,我們需要安裝Flask pip軟件包: pip install -U Flask
接下來,使用你喜歡的文本編輯器在服務器文件夾中創建一個名為“app.py”的文件,并添加以下代碼以創建基本的API:
from flask import Flask, jsonify, make_response, request, abort
app=Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__=="__main__":
app.run()
現在打開終端并運行python3 app.py以啟動服務器。這將主要在5000端口上啟動服務器。為了測試API,請在瀏覽器中打開此鏈接:http://localhost:5000/
你應該在瀏覽器中打印出Hello World。如果不是,則在啟動API時檢查API是否在其他端口上運行或在終端上打印錯誤。
我們將調用POST API,因此最好在繼續進行之前安裝Postman工具。使用此工具將向服務器發送POST請求。
接下來,我們需要使用以下命令安裝catboost,pandas和Flask-Cors pip軟件包:
pip install catboost pandas Flask-Cors
接下來,將我們在第1部分末尾下載的經過訓練的模型的pickle文件(finalized_model.sav)復制到服務器文件夾中。
現在,使用以下代碼更新 app.py:
from flask import Flask, jsonify, make_response, request, abort
import pandas as pd
import catboost
import pickle
from flask_cors import CORS,cross_origin
model=pickle.load(open( "finalized_model.sav", "rb"))
app=Flask(__name__)
app.config['CORS_HEADERS']='Content-Type'
cors=CORS(app)
@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'error': 'Not found'}), 404)
@app.route("/")
def hello():
return "Hello World!"
@app.route("/get_prediction", methods=['POST','OPTIONS'])
@cross_origin()
def get_prediction():
if not request.json:
abort(400)
df=pd.DataFrame(request.json, index=[0])
cols=["CONSOLE","RATING","CRITICS_POINTS","CATEGORY","YEAR","PUBLISHER","USER_POINTS"]
df=df[cols]
return jsonify({'result': model.predict(df)[0]}), 201
if __name__=="__main__":
app.run()
在第6行中,我們將訓練后的模型導入到我們的python文件中。
在第10行,我們初始化CORS模塊以允許來自客戶端API調用的請求。
在第11行,我們定義了一個錯誤處理程序,如果從服務器訪問了任何未處理的異常或未定義的路徑,它將發送錯誤響應。
對我們來說,主要的興趣點是從第19行定義的get_prediction POST API。get_prediction方法是我們從客戶端獲取數據并提供響應的銷售預測。
在第24行,我們將來自API請求的數據轉換為pandas數據框?,F在,我們的模型期望列以特定順序提供正確的響應。因此,在第25行中,我們指定列順序。在接下來的步驟中,以所需順序重新排列列。
在第27行,model.predict用于從模型中獲取預測,并將其作為響應傳遞給客戶端。在這一步,我們準備好在本地使用該API。我們可以通過發送POST-API調用來測試Postman客戶端中的API,如截圖所示:
你可以在上述請求的正文部分中添加一個JSON示例,可以在代碼Github-gist中找到。
確保在主體和主體類型中選擇raw和JSON選項(如屏幕截圖所示),并在請求類型中選擇POST。
如果在此步驟之前一切正常,那么恭喜你,你現在有了一個后端API,該API可根據輸入參數根據經過訓練的模型進行預測。
額外提示
在后端設計中不建議在單個文件中編寫API,我們可以將路徑和模型導入分隔到不同的文件夾中,以使代碼更具模塊化。如果將來引入其他API,這也將使我們能夠以可管理的方式擴展代碼。
在這一點上,我們可以再次休息一下,確保收藏了本文,以便輕松地重新開始本項目的下一部分。
到目前為止,我們的API在本地工作,但我們需要將其部署在遠程服務器上,以供其他地方使用。為此,我們將使用Heroku作為我們的API托管平臺。
我主要參考了來自stackabuse 的文章,將其部署到Heroku。我們將簡要介紹這些步驟,但是如果你卡在了其中某個步驟,請在此處參閱原始文章:
首先,我們使用terminal命令安裝gunicorn:
pip install gunicorn
接下來,運行下面的命令將所有已安裝的pip包,存儲到require.txt文件:
pip freeze > requirements.txt
你可以參考此處上傳的requirements.txt文件以供參考:
接下來,Procfile使用以下代碼在服務器文件夾中創建一個名稱為Procfile的文件:
web: gunicorn app:app
現在,在Heroku網站上注冊,在該網站上創建一個應用,然后按照原始文章中的說明安裝Heroku CLI 。
接下來,通過運行以下命令從本地終端登錄Heroku: heroku login -i
使用以下命令添加你的Heroku應用git參考:
heroku git:remote -a {your-project-name}
現在,使用以下命令將代碼推送到Heroku:
git push heroku master
在運行上述命令的最后,你將在終端輸出中獲得API URL,現在我們可以使用該URL從客戶端進行調用。此時,我們還可以從PostMan應用程序發送API請求,以查看我們是否正確收到了與步驟2末尾所述類似的響應。
到目前為止的代碼庫可以在以下Github存儲庫中找到
現在,我們在服務器上托管了一個正常工作的API。如果一切正常,那么我們可以繼續開發客戶端應用程序。如果遇到任何問題,請在評論部分中提及你的問題。
我們將需要在我們的計算機上正確安裝和設置Node.js。因此,請先下載并安裝適用于你相關操作系統和系統的Node.js,然后再繼續進行操作。另外,建議安裝yarn管理器:yarn 安裝
現在,在上一步中創建frontend的服務器文件夾外部創建一個新文件夾,并從終端進入該frontend文件夾內部。
接下來,我們將創建一個新的react應用程序,并通過在終端中運行以下命令來啟動它:
npx create-react-app sales-prediction-app
cd sales-prediction-app
npm start
你應該在瀏覽器中看到默認打開的瀏覽器選項卡,以及react.js默認模板應用程序?,F在我們需要在我們最喜歡的編輯器中打開該項目(我正在使用VSCode),并開始進行更改以構建前端應用程序。
首先,我們需要在應用程序公共文件夾中的index.html文件中導入相關的引導程序文件。
我們需要按照bootstrap文檔提供的說明在index.html文件中添加文件,如下所示:
<head>
...
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
...
</head>
<body>
...
<div id="root"></div>
...
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
...
</body>
我們的最終用戶界面是集合下拉菜單項,其中單個項如下所示:
我們將在src文件夾中創建一個名稱為optionsSources.json的JSON文件。JSON文件中的每個條目都包含以下對象:
{
"CONSOLE": {
"options": [
"ps2","x360","ps3","pc"
],
"icon": "?",
"dropDownPlaceholder": "Select Console"
}
}
下拉菜單中顯示的選項位于options數組中,下拉菜單選項左側顯示的圖標和標簽位于icon和dropDownPlaceholder項。我們需要創建多個這樣的下拉列表,因此要添加的完整JSON文件如以下文件所示:
接下來,我們需要在我們的應用程序中實現下拉組件。在src文件夾中創建一個名為components的文件夾,并在components文件夾中創建一個名為OptionSelection.js的文件
我們將編寫一個功能組件,該組件返回一個下拉項,如下所示:
import React,{ useState } from 'react';
import optionSources from '../optionsSources.json';
function OptionSelection({itemKey, setOptionInObject}) {
const title=optionSources[itemKey].dropDownPlaceholder;
const icon=optionSources[itemKey].icon;
return(
<div className="d-flex justify-content-start align-items-center mt-2 selection-item">
<div className="option-label">
<b><span role="img" aria-label="label-icon">{icon}</span>{` ${title}`}</b>
</div>
<div className="dropdown ml-4">
<button className="btn btn-primary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{title}
</button>
<div className="dropdown-menu" aria-labelledby="dropdownMenuButton">
{renderOptionsDropdown()}
</div>
</div>
</div>
)
}
export default OptionSelection;
在上面的組件中,我們itemKey從第3行的父組件中獲得prop(param)值。我們假設itemKey從父組件接收的是CONSOLE。在第4行和第5行,我們首先提取顯示在下拉菜單左側的標題和圖標。然后,根據Boostrap文檔在創建下拉列表時,在第6行的返回函數中使用HTML標簽。
接下來,我們需要實現renderOptionsDrop在返回函數中定義的函數,如下所示:
import optionSources from '../optionsSources.json';
function OptionSelection({itemKey, setOptionInObject}) {
...
const renderOptionsDropdown=()=> {
const selectionOptions=optionSources[itemKey].options;
return selectionOptions.map((selectionOption, index)=>{
return (
<div className="dropdown-item pointer"
key={`${index}${selectionOption}`}
onClick={()=> handleDropDownSelection(selectionOption)}
>
{selectionOption}
</div>
);
})
}
...
}
在第5行,我們從optionSources JSON對象獲取特定項的options數組,并將其存儲在selectionOptions變量中。
然后在第6行,我們使用map函數迭代數組并顯示下拉選擇項。我們必須在第10行使用onClick函數更新下拉項的選定值。
然后實現onClick處理程序中的函數handleDropDownSelection,如下所示:
import React,{ useState } from 'react';
...
function OptionSelection({itemKey, setOptionInObject}) {
const [currentSelectedOption, setSelectedOption]=useState(null);
const handleDropDownSelection=(consoleOption)=> {
setSelectedOption(consoleOption)
setOptionInObject(itemKey, consoleOption)
}
...
}
我們在第1行輸入了useState hook。它是一個內部函數,允許我們使用狀態變量的概念動態更新值。關于這個函數的更多信息可以在這里找到:
在第7行,我們更新下拉列表的選定選項。在第8行中,我們將選擇的值傳遞回父函數以進行進一步處理。
這個組件的完整代碼可以在這里找到:https://github.com/codeclassifiers/video-salesprediction-frontend/blob/master/src/components/ConsoleSelection.js
然后我們在src文件夾中導入此選項并對服務器進行API調用。完整的代碼可以在這里找到:
然后在handleInputSubmission函數中對后端進行API調用,如下所示:
import React, {useState} from 'react';
import axios from 'axios';
function App() {
...
const handleInputSubmission=()=> {
if(selectedObject && Object.keys(selectedObject).length===7) {
...
axios.post(process.env.REACT_APP_HEROKU_SERVER_URL, selectedObject)
.then(function (response) {
setPredictionLoading(false)
setModelPrediction(response.data.result)
})
.catch(function (error) {
setPredictionLoading(false)
setRequestFailed("Some error ocurred while fetching prediction")
});
} else {
setRequestFailed("Please select all fields before submitting request")
}
}
}
我們正在使用Axios npm模塊對后端Heroku服務器進行POST API調用。確保在process.env.REACT_APP_HEROKU_SERVER_URL占位符的第8行上添加自己的Heroku服務器URL,以接收來自服務器API的響應。
最好將API URL變量保存在.env文件中,然后在部署環境中進行設置。可以在這里找到更多詳細信息:
在此處找到Github上的前端應用程序的完整資源:
這使我們有了在線部署Web應用程序的最后一步。因此,請耐心一些,讓我們開始項目的最后一步。
Netlify是一個可以輕松在線部署靜態網站的平臺。在部署使用createreact app模塊生成的應用程序時,它有一個非常簡單的過程。我們將利用此服務在線托管我們的web應用程序。
首先,我們需要在Github上創建一個帳戶。
然后,我們需要將前端文件夾上傳到Github存儲庫。我們可以按照官方文檔中顯示的步驟將項目部署到Github:官方文檔(https://docs.github.com/en/github/importing-your-projects-to-github/adding-an-existing-project-to-github-using-the-command-line)
一旦該項目在GitHub上進行部署,通過遵循以下官方文檔即可簡單,直接地完成netlify的過程:Netlify Deploy(https://www.netlify.com/blog/2016/09/29/a-step-by-step-guide-deploying-on-netlify/)
如果你已在上一步中將環境變量用于服務器URL,請確保如本文檔所示將其添加到netlify dashboard中。
最后,我們將提供一個如下所示的網絡應用程序:
額外提示
老實說,上面的UI非常簡單。它沒有很好的配色方案(主要是因為像我這樣的開發人員不是優秀的設計師)。你可以改善設計并調整CSS,以更好地查看網頁。
這樣就完成了從機器學習hackathon數據集創建銷售預測Web應用程序的過程。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。