1. 程式人生 > >tornado框架的學習與應用

tornado框架的學習與應用

簡單介紹一下所使用的高併發框架tornado,它是一個用python編寫的可擴充套件的非阻塞式web伺服器及其相關工具的開源框架,在處理嚴峻的網路流量時表現得足夠強健,但卻在建立和編寫時有著足夠的輕量級,並能夠被用在大量的應用和工具中。
先簡單介紹一下
用tornado實現的經典helloworld程式如下:

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world"
) application = tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": application.listen(8880) tornado.ioloop.IOLoop.instance().start()

伺服器程式執行之後,電腦訪問本地迴環地址即可看見:
hello

另一個全功能的helloworld程式如下:

import tornado.httpserver
import tornado.ioloop
import tornado.options
import
tornado.web from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) class IndexHandler(tornado.web.RequestHandler): def get(self): greeting = self.get_argument('greeting', 'Hello') self.write(greeting + ', friendly user!') if
__name__ == "__main__": tornado.options.parse_command_line() app = tornado.web.Application(handlers=[(r"/", IndexHandler)]) http_server = tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()

命令列執行:
$ python hello.py --port=8000
即可在指定埠啟動伺服器
同樣,無參訪問伺服器地址會顯示預設引數:
1

攜帶引數訪問,如輸入:localhost:8000/?dairen會顯示如下:
2

下面看一個處理字串的伺服器程式:

import textwrap

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class ReverseHandler(tornado.web.RequestHandler):
    def get(self, input):
        self.write(input[::-1])

class WrapHandler(tornado.web.RequestHandler):
    def post(self):
        text = self.get_argument('text')
        width = self.get_argument('width', 40)
        self.write(textwrap.fill(text, int(width)))

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[
            (r"/reverse/(\w+)", ReverseHandler),
            (r"/wrap", WrapHandler)
        ]
    )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

這個程式有2個handler,一個是反轉輸入的字串,引數input將包含匹配處理函式正則表示式第一個括號裡的字串。一個是以指定的寬度裝飾文字(預設40),並將結果字串寫回到HTTP響應中。
同樣執行伺服器:
$ python string_service.py --port=8000
可以用2種方式訪問:

$ curl http://localhost:8000/reverse/dairen
neriad

3

$ http://localhost:8000/wrap -d text=Lorem+ipsum+dolor+sit+amet,+consectetuer+adipiscing+elit.
Lorem ipsum dolor sit amet, consectetuer
adipiscing elit.

tornado的狀態碼:
你可以使用RequestHandler類的set_status()方法顯式地設定HTTP狀態碼。然而,你需要記住在某些情況下,Tornado會自動地設定HTTP狀態碼。下面是一個常用情況的綱要:

404 Not Found
Tornado會在HTTP請求的路徑無法匹配任何RequestHandler類相對應的模式時返回404(Not Found)響應碼。

400 Bad Request
如果你呼叫了一個沒有預設值的get_argument函式,並且沒有發現給定名稱的引數,Tornado將自動返回一個400(Bad Request)響應碼。

405 Method Not Allowed
如果傳入的請求使用了RequestHandler中沒有定義的HTTP方法(比如,一個POST請求,但是處理函式中只有定義了get方法),Tornado將返回一個405(Methos Not Allowed)響應碼。

500 Internal Server Error
當程式遇到任何不能讓其退出的錯誤時,Tornado將返回500(Internal Server Error)響應碼。你程式碼中任何沒有捕獲的異常也會導致500響應碼。

200 OK
如果響應成功,並且沒有其他返回碼被設定,Tornado將預設返回一個200(OK)響應碼。


》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
下面介紹我的基於tornado的伺服器專案:qq聊天群的實現
專案程式碼分為主要是2部分:
後臺伺服器python實現+前端(html靜態檔案+css格式+JavaScript客戶端程式碼)

首先來看伺服器的實現:
首先基於tornado.web.RequestHandler類實現自己的第一個請求處理類,如下:

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html", messages=ChatSocketHandler.cache, clients=ChatSocketHandler.waiters, username= "遊客%d" % ChatSocketHandler.client_id)

其中用render方法引入模板index.html ,不僅僅是要引用模板網頁,還要向這個網頁傳遞一些資料,包括:
歷史訊息:messages
線上使用者:clients
分配給當前訪問的使用者的使用者名稱:username
由於想要實現的qq聊天群需要雙向web通訊,也就是不僅僅伺服器響應客戶端,客戶端也能響應伺服器傳送的訊息,所以又基於tornado.websocket.WebSocketHandler類實現了自己的websocket處理類,
Tornado在websocket模組中提供了一個WebSocketHandler類。這個類提供了和已連線的客戶端通訊的WebSocket事件和方法的鉤子。當一個新的WebSocket連線開啟時,open方法被呼叫,而on_message和on_close方法分別在連線接收到新的訊息和客戶端關閉時被呼叫。
此外,WebSocketHandler類還提供了write_message方法用於向客戶端傳送訊息,close方法用於關閉連線。
在這裡我把使用者的訊息型別分為3種,一種是上線:online,一種是下線:offline 一種是使用者發訊息了:message

每當有新的使用者連線時,給其分配使用者名稱,並把該使用者加入到已連線使用者列表中:

def open(self):
    self.client_id = ChatSocketHandler.client_id
    ChatSocketHandler.client_id = ChatSocketHandler.client_id + 1
    self.username = "遊客%d" % self.client_id
    ChatSocketHandler.waiters.add(self)

然後封裝一個使用者上線的訊息字典,專門去處理:

chat = {
     "id": str(uuid.uuid4()),
     "type": "online",
     "client_id": self.client_id,
     "username": self.username,
     "datetime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
     }
 ChatSocketHandler.send_updates(chat)

同樣,使用者下線:刪除使用者,廣播下線訊息
使用者發訊息:更新歷史訊息列表,廣播訊息
使用者傳送的訊息會以json的格式收到,所以要先解析,然後提取其中的資訊,包裝成我們自己的chat字典,然後群發:

parsed = tornado.escape.json_decode(message)
self.username = parsed["username"]
chat = {
    "id": str(uuid.uuid4()),
    "body": parsed["body"],
    "type": "message",
    "client_id": self.client_id, 
    "username": self.username,
    "datetime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    }

其中廣播訊息也就是向線上的每一個使用者,使用websocket的功能傳送一次訊息。

寫好了open on_close on_message類之後,下面考慮我想要實現的歷史記錄的實現,可以快取1000條訊息記錄放在一個列表中(列表中每一項是一個chat的字典型別),每次新使用者訪問就把這個列表[]發給他,讓他也能看到歷史記錄,每當記錄滿了,那就擷取最後1000條儲存下來:

if len(cls.cache) > cls.cache_size:
            cls.cache = cls.cache[-cls.cache_size:]

這裡的cache_size我定義為1000,也就是快取1000條訊息記錄。

另外,為了完善對伺服器執行過程的掌控,加入python日誌模組,也就是匯入logging模組,

 logging.info("sending message to %d waiters", len(cls.waiters))

這樣控制檯就可以觀察到執行的一些操作了,當然也可以把日誌寫進檔案中以後查閱。

至此,我們伺服器的大致內容設計的差不多了,下面考慮前端顯示問題,以及基於websocket通訊的JavaScript客戶端程式設計問題
首先想一想聊天室主頁的顯示情況,首先要有使用者列表,實時顯示線上的使用者,還要有歷史訊息列表,顯示我們快取的1000條聊天記錄,另外就是像qq群一樣的聊天介面,你一句我一句,要有使用者名稱,訊息體,時間,然後最下面還要有我們自己的聊天資訊輸入框與傳送的地方,差不多就這些吧
先分析主頁模板html檔案怎麼寫吧,使用者列表:

        <div id="users">
             線上使用者:<p/>
             <ul id="user_list">
        {% for client in clients %}
        <li id={{client.client_id}}>{{client.username}}</li>
        {% end %}
             </ul>
        </div>

這樣就會顯示伺服器傳給html檔案裡的clients 的所有線上使用者。再看我們聊天介面顯示的每一條訊息,顯然按格式包裝起來會更方便,所以每條訊息就以一個格式包裝與顯示:

<div class="message" id="m{{ message["id"] }}">{{message["username"]}} 說:{% module linkify(message["body"]) %} ({{message["datetime"]}})</div>

再看歷史訊息的顯示格式,歷史訊息也是新使用者上線的時候伺服器在渲染模板html的時候加入的引數,html模板中大致以如下格式進行渲染:

  <div id="inbox">
    {% for message in messages %}
      {% include "message.html" %}
    {% end %}
  </div>

最後就是我們的訊息內容輸入與傳送,這一部分需要伺服器JavaScript程式碼把我們使用者輸入的訊息接收處理,然後發給我們的伺服器,伺服器進行解析後給與響應,html格式大致如下:

        <form action="/a/message/new" method="post" id="messageform">
          <table>
            <tr>
          <td >使用者名稱:</td>
              <td><input name="username" id="username" style="width:100px" value="{{username}}"></td>
        </tr>
        <tr>
          <td>輸入訊息:</td>
          <td><input name="body" id="message" style="width:500px"></td>
        </tr>
        <tr>
              <td style="padding-left:5px">
                <input type="submit" value="提交">
                <input type="hidden" name="next" value="{{ request.path }}">
                {% module xsrf_form_html() %}
              </td>
            </tr>
          </table>
        </form>

這裡使用了HTML表單,表單用於客戶端收集使用者在瀏覽器的輸入,是實現客戶端與伺服器互動的核心方法,注意:

{% module xsrf_form_html() %}

這裡我使用了tornado的防止跨站攻擊的手段,非常簡單,要麼在例項化tornado.web.Application的時候傳入引數:xsrf_cookies=True,要麼在每個具有HTML表單的模板檔案中,為所有HTML表單新增xsrf_form_html()函式標籤,而{% module xsrf_form_html() %}這句話起到了為表單新增隱藏元素,以防止跨站請求的作用。
這一句話作用挺大,也可以看出tornado為開發者減輕了很多的負擔,加快了開發效率,不然就要自己考慮很多細節性的工作。

至此,我們關於主頁模板的內容基本介紹完畢,下面就是介紹客戶端的JavaScript部分,我們的qq聊天室是基於websocket的全雙工的,而websocket是html5的標準之一,所以主流的瀏覽器的JavaScript程式語言是支援websocket程式設計的,方法也很簡單,就是圍繞著websocket物件,使用:

socket = new WebSocket(url);

就可根據url,新建一個websocket連線,然後與之通訊,然後響應websocket的如下事件:

websocket.onopen(),
websocket.onmessage(),
websocket.onerror(),
websocket.onclose(),被動關閉連線

然後websocket還有2種主動事件型別:
websocket.send()
websocket.close():主動關閉連線
下面開始我的客戶端部分的設計,網頁被載入的時候,瀏覽器會建立頁面文件物件模型DOM樹,JavaScript中幾乎所有的內容都是基於DOM樹來進行的,而DOM樹的根節點是document,JavaScript通過操作DOM樹,進行 增刪改查,實現對html的操作,在本專案的JavaScript程式碼中,我使用了最優秀的js客戶端框架庫jQuery,jQuery能方便的處理html,響應事件,實現動畫效果,可以把它認為是html css JavaScript的封裝,使用jQuery可以讓開發者更輕鬆地寫JavaScript程式碼。
我這裡直接在html原始檔中引用internet上的jQuery庫連線,更輕量:

<script src="{{ static_url("jquery.min.js") }}" type="text/javascript"></script>

而我們在JavaScript程式碼中引用jQuery庫也很方便,就是一個美元的符號:$
形如:
$(selector).action()
這裡selector是選擇器,action就是要進行的操作。jQuery中的選擇器與css中的選擇器很類似,就是按標記名,id等進行選擇,另外還可以根據特定的屬性,根據標記相對於父標記的位置,元素內容等進行選擇。
下面直奔主題,首先要處理我們的:文件全部載入完成的事件,也就是:
$(document).ready(function()
{
}
jQuery中把html事件中的on都給去掉了,譬如onclick事件,在jQuery中就是click事件,
這個函式,首先要實現傳送表單:messageform的submit事件:

$("#messageform").live("submit", function() {
    newMessage($(this));
    return false;
});

一旦有message提交了,立馬執行newMessage函式,也就是給伺服器發訊息
下面同樣的作用,只不過是監控keyCode == 13的按鍵,也就是我們鍵盤上的enter鍵

$("#messageform").live("keypress", function(e) {
    if (e.keyCode == 13) {
        newMessage($(this));
        return false;
    }
});

其中的newmessage函式實現如下:

function newMessage(form) {
    var message = form.formToDict();
    updater.socket.send(JSON.stringify(message));
    form.find("input[type=text]").val("").select();
}

就是向伺服器以字典的格式,傳送一個新的訊息,其中的formToDict 函式實現如下:

formToDict = function() {
    var fields = this.serializeArray();
    var json = {}
    for (var i = 0; i < fields.length; i++) {
        json[fields[i].name] = fields[i].value;
    }
    if (json.next) delete json.next;
    return json;
};

作用是把表單中所有的輸入儲存到json物件中去,最後返回客戶端要發給伺服器的訊息字典,也是一個json物件。

另外我這裡設定了一個,一旦選中了我們之前html檔案裡定義的id為message編輯框的控制元件,就開始發起一個連線,動作如下:

$("#message").select();
updater.start();

其中,start()內容大致為:

function() 
{
        var url = "ws://" + location.host + "/chatsocket";
        updater.socket = new WebSocket(url);
        updater.socket.onmessage = function(event) {
            updater.showMessage(JSON.parse(event.data));
        }
}

這裡一旦選中了編輯框,客戶端js程式碼就開始新建一個websocket連線,其中url 就是我們伺服器的地址,並且設定了我們的onmessage()函式,也就是響應伺服器訊息的函式,其內容大致如下:

showMessage: function(message) {
    del(message.client_id);
    if (message.type!="offline")
    {
        add(message.client_id, message.username);
        if (message.body=="") return;
        var existing = $("#m" + message.id);
        if (existing.length > 0) return;
        var node = $(message.html);
        node.hide();
        $("#inbox").append(node);
        node.slideDown();
    }
}

當收到一個伺服器的訊息,先去分析訊息的型別:online offline 還是 message型別,分別做不同的響應,我的實現是先不管訊息型別,先把當前的使用者給刪了,呼叫del函式來動態刪除線上使用者列表,del函式如下:
動態刪除:

function del(id){
    $('#'+id).remove();
}

然後再來分析訊息型別是不是offline,如果是offline,那就結束了
如果不是offline,那就是上線或者發訊息啦,所以再把剛剛刪的使用者新增到使用者名稱列表中,呼叫add函式,如下:
動態新增:

function add(id,txt) {    
    var ul=$('#user_list');    
    var li= document.createElement("li");    
    li.innerHTML=txt;  
    li.id=id;  
    ul.append(li);    
} 

其中$(‘#user_list’)就是使用jQuery的查詢器進行元素定位,查詢id為user_list的HTML標籤,之後就可以通過append(),remove來動態的新增刪除列表元素了。

接著判斷訊息是不是無效,也就是訊息體為空,那就直接return,這樣就實現了
使用者下線:列表中刪掉使用者
使用者上線(一旦動了編輯框就會上線),但沒發訊息:先刪後加,重新整理了一下
然後再實現使用者發訊息的過程,那就比較簡單了,就是:

 var node = $(message.html);
 node.hide();
 $("#inbox").append(node);
 node.slideDown();

先選中了訊息,為新收到的訊息建立一個新的節點node,先隱藏訊息(因為傳送出去要清空的啊),然後把訊息新增到我們的html檔案中的inbox標籤的尾部,進行顯示,inbox標籤內容如下:

<div id="inbox">
  {% for message in messages %}
    {% include "message.html" %}
  {% end %}
</div>

然後把聊天介面下拉到最下面,至此就可以動態的處理:顯示伺服器傳送的訊息了,也就是所有的客戶端都能正確顯示伺服器廣播的訊息了。

最後就是把我們的伺服器JavaScript程式碼嵌入到主頁的HTML中,一般的嵌入方式有內部嵌入和外部連結兩種方式,我這裡把JavaScript程式碼單獨放在了一個檔案裡,使用了外部連結的方式嵌入到了html檔案中:

<script src="{{ static_url("chat.js") }}" type="text/javascript"></script>

這裡chat.js就是我的js程式碼檔案。
還有別忘了在嵌入js之前,嵌入我們使用的jQuery庫,所以應該是這個樣子:

<script src="{{ static_url("jquery.min.js") }}" type="text/javascript"></script>
<script src="{{ static_url("chat.js") }}" type="text/javascript"></script>

至此,我的qq群的前端+後臺已經基本實現,
python mychat.py --port=8880
在8880埠執行伺服器之後,在本地用迴環地址localhost:8880訪問:
11

22

或者別的主機使用
ip:port的形式,都能訪問該聊天群,伺服器所在ip地址,也就是我的電腦ip是:115.156.245.122

我這裡使用師兄的電腦登入了一下隨便傳送了一條訊息:
33

44

同時由於我們伺服器加入了logging日誌模組,所以在控制檯可以看到我想要列印的訊息,例如:
55

》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

至此,該專案以完成我想要的一個qq聊天群的所有基本功能了。但是專案改進的餘地還很大,我總結一下,
不足主要有3點:
1、由於沒有使用者持久化的訪問需求,都是立馬連立馬走,所以沒有使用資料庫,如果設計一個qq好友系統。可以考慮加入資料庫,把使用者以及使用者關係,歷史訊息,使用者名稱密碼等等都儲存在資料庫中
2、由於前端知識只是略懂,所以介面略微簡陋。。。有需要可以深入學習前端的知識
3、功能可以更加豐富化,可以加入群許可權控制,使得有類似於管理員一樣的存在,還可以加入群檔案共享,群公告,群照片等等。。。

相關推薦

tornado框架學習應用

簡單介紹一下所使用的高併發框架tornado,它是一個用python編寫的可擴充套件的非阻塞式web伺服器及其相關工具的開源框架,在處理嚴峻的網路流量時表現得足夠強健,但卻在建立和編寫時有著足夠的輕量級,並能夠被用在大量的應用和工具中。 先簡單介紹一下 用t

深度學習框架Tensorflow學習應用(5到8)

五. 03-1 迴歸 # coding: utf-8 # In[2]: import tensorflow as tf import numpy as np import matplotlib.pyplot as plt # In[3]: #使用numpy生成200個隨機點 x_

10、深度學習框架Caffe學習應用--訓練結果影象分析

一、觀察損失曲線:學習率  橫軸:輪。 縱軸:損失。  黃色:學習率太高;  藍色:學習率太低;  綠色:學習率高了;  紅色:學習率最好;  二、放大損失曲線:學習率、batch大小 沒有呈現線性:說明學習率低了。 下降太慢:說明學習率太高。 寬度

深度學習框架Tensorflow學習應用(八 儲存和載入模型,使用Google的影象識別網路inception-v3進行影象識別)

一 模型的儲存 [email protected]:~/tensorflow$ cat 8-1saver_save.py # coding: utf-8 # In[1]: import tensorflow as tf from tensorflow.examples.tutorials

深度學習框架tensorflow學習應用10(MNSIT卷積神經網路實現)

  import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('F:/PY/MNIST_data/',

深度學習框架tensorflow學習應用9(tensorboard視覺化)

import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data from tensorflow.contrib.tensorboard.plugins import projector # In

深度學習框架tensorflow學習應用8(tensorboard網路結構)

#載入資料集 mnist = input_data.read_data_sets("MNIST_data",one_hot=True) #如果沒有就下載,然後以獨熱碼的形式載入,有的話就不下載 #每個批次的大小 batch_size =100 #計算一共有多少個批次 n_batch = mnis

深度學習框架tensorflow學習應用7(改變模型和優化器提升準確率)

#訓練 train_step = tf.train.AdamOptimizer(lr).minimize(loss) 原來的程式碼: # coding: utf-8 # In[ ]: import tensorflow as tf from tensorflow.examples.tu

深度學習框架tensorflow學習應用6(優化器SGD、ADAM、Adadelta、Momentum、RMSProp比較)

看到一個圖片,就是那個表情包,大家都知道: Adadelta  》  NAG 》 Momentum》 Remsprop 》Adagrad 》SGD 但是我覺得看情況而定,比如有http://blog.51cto.com/12568470/1898367常見優化演

深度學習框架tensorflow學習應用6(防止過擬合dropout,keep_prob =tf.placeholder(tf.float32))

import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data # In[3]: # 載入資料集 mnist = input_data.read_data_sets("MNIST_data",

深度學習框架tensorflow學習應用5(softmax函式+交叉熵代價函式和二次代價函式的比較)

import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data # In[3]: #載入資料集 mnist = input_data.read_data_sets("MNIST_data",o

深度學習框架tensorflow學習應用4(MNIST資料集分類的簡單版本示例)

資料集 我們要訓練機器學習, 那麼就要用到訓練資料. 這次我們使用MNIST_data資料集 在程式中要匯入該資料集, 語句:mnist = input_data.read_data_sets("MNIST_data", one_hot=True)one_hot 意思是把資料集變成[

深度學習框架tensorflow學習應用3(非線性迴歸訓練示例)

在瞭解了迴歸演算法中的正向傳播和反向傳播之後, 我們可以用梯度下降法來進行一個非線性迴歸的示例. 此次示例中, 我們設定輸入樣本神經元只有一個, 中間神經元有10個, 輸出神經元1個.               &

深度學習框架tensorflow學習應用2(fetch and feed和訓練一元一次方程擬合示例 )

Fetch Fetch操作是指TensorFlow的session可以一次run多個op 語法: 將多個op放入陣列中然後傳給run方法 Feed Feed操作是指首先建立佔位符, 然後把佔位符放入op中. 在run op的時候, 再把要op的值傳進去,

深度學習框架TensorFlow學習應用(三)——使用交叉熵作為代價函式

二次代價函式(quadratic cost): 其中,C表示代價函式,x表示樣本,y表示實際值,a表示輸出值,n表示樣本的總數。 例如: 假如我們使用梯度下降法(Gradient descent)來調整權值引數的大小,權值w和偏置b的梯

【深度學習框架Caffe學習應用】 第十一課

1.車輛檢測實踐:使用Caffe訓練的深度學習模型做目標檢測——以車輛檢測為例 有關檔案都放在以下資料夾中: 對vehicle_detetc.cpp進行編譯: 編譯之前,需要修改一下程式碼中的檔

【深度學習框架Caffe學習應用】第三課 將圖片資料轉化為LMDB資料``

1.將圖片資料轉化為LMDB資料 第一步:建立圖片檔案列表清單,一般為一個txt檔案,一行一張圖片 我在caffe/data/目錄下新建一個test_data的資料夾,裡面放訓練集及資料集

深度學習框架TensorFlow學習應用(五)——TensorBoard結構視覺化

一、TensorBoard網路結構 舉例: import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data #載入資料集 mnist=input_dat

【深度學習框架Caffe學習應用】第四課 Caffe視覺化工具

1.首先準備pycaffe環境 輸入一下命令: 2.網路視覺化的工具 2.1在caffe中,有一個專門用於畫網路結構圖的py檔案:caffe/tools/draw_net.py 2

深度學習框架TensorFlow學習應用(四)——擬合問題、優化器

一、擬合 1)迴歸問題: 過擬合儘量去通過每一個樣本點,誤差為零。假如有一個新的樣本點: 會發現過擬合的偏差會很大。 2)分類問題: 同樣的當得到新的樣本點後,過擬合的錯誤率可能會提高。 3)防止過擬合: 1.增加資料集 2.正則化方