<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 如何開發Keras序列到序列預測的編碼器 - 解碼器模型 > 原文: [https://machinelearningmastery.com/develop-encoder-decoder-model-sequence-sequence-prediction-keras/](https://machinelearningmastery.com/develop-encoder-decoder-model-sequence-sequence-prediction-keras/) 編碼器 - 解碼器模型提供了使用循環神經網絡來解決具有挑戰性的序列到序列預測問題(例如機器翻譯)的模式。 可以在Keras Python深度學習庫中開發編碼器 - 解碼器模型,并且在Keras博客上描述了使用該模型開發的神經機器翻譯系統的示例,其中示例代碼與Keras項目一起分發。 這個例子可以為您自己的序列到序列預測問題提供編碼器 - 解碼器LSTM模型的基礎。 在本教程中,您將了解如何使用Keras為序列到序列預測問題開發復雜的編碼器 - 解碼器循環神經網絡。 完成本教程后,您將了解: * 如何在Keras中正確定義復雜的編碼器 - 解碼器模型以進行序列到序列預測。 * 如何定義可用于評估編碼器 - 解碼器LSTM模型的人為但可擴展的序列到序列預測問題。 * 如何在Keras中應用編碼器 - 解碼器LSTM模型來解決可伸縮的整數序列到序列預測問題。 讓我們開始吧。 ![How to Develop an Encoder-Decoder Model for Sequence-to-Sequence Prediction in Keras](img/aa2a843b49a185a698da7748e1ce1131.jpg) 如何開發Keras序列到序列預測的編碼器 - 解碼器模型 照片由[Bj?rnGro?](https://www.flickr.com/photos/damescalito/35370558025/),保留一些權利。 ## 教程概述 本教程分為3個部分;他們是: * Keras中的編碼器 - 解碼器模型 * 可擴展的序列到序列問題 * 用于序列預測的編碼器 - 解碼器LSTM ### Python環境 本教程假定您已安裝Python SciPy環境。您可以在本教程中使用Python 2或3。 您必須安裝帶有TensorFlow或Theano后端的Keras(2.0或更高版本)。 本教程還假設您安裝了scikit-learn,Pandas,NumPy和Matplotlib。 如果您需要有關環境的幫助,請參閱此帖子: * [如何使用Anaconda設置用于機器學習和深度學習的Python環境](https://machinelearningmastery.com/setup-python-environment-machine-learning-deep-learning-anaconda/) ## Keras中的編碼器 - 解碼器模型 編碼器 - 解碼器模型是一種組織序列到序列預測問題的循環神經網絡的方法。 它最初是為機器翻譯問題而開發的,盡管它已經證明在相關的序列到序列預測問題上是成功的,例如文本摘要和問題回答。 該方法涉及兩個循環神經網絡,一個用于編碼源序列,稱為編碼器,第二個用于將編碼的源序列解碼為目標序列,稱為解碼器。 Keras深度學習Python庫提供了一個如何實現機器翻譯的編碼器 - 解碼器模型的例子( [lstm_seq2seq.py](https://github.com/fchollet/keras/blob/master/examples/lstm_seq2seq.py) ),由庫創建者在帖子中描述:“[十分鐘的介紹在Keras](https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html) 中進行序列到序列學習。“ 有關此型號的詳細分類,請參閱帖子: * [如何定義Keras神經機器翻譯的編碼器 - 解碼器序列 - 序列模型](https://machinelearningmastery.com/define-encoder-decoder-sequence-sequence-model-neural-machine-translation-keras/) 有關使用return_state的更多信息,可能是您的新手,請參閱帖子: * [了解Keras中LSTM的返回序列和返回狀態之間的差異](https://machinelearningmastery.com/return-sequences-and-return-states-for-lstms-in-keras/) 有關Keras Functional API入門的更多幫助,請參閱帖子: * [如何使用Keras功能API進行深度學習](https://machinelearningmastery.com/keras-functional-api-deep-learning/) 使用該示例中的代碼作為起點,我們可以開發一個通用函數來定義編碼器 - 解碼器循環神經網絡。下面是這個名為 _define_models()_的函數。 ```py # returns train, inference_encoder and inference_decoder models def define_models(n_input, n_output, n_units): # define training encoder encoder_inputs = Input(shape=(None, n_input)) encoder = LSTM(n_units, return_state=True) encoder_outputs, state_h, state_c = encoder(encoder_inputs) encoder_states = [state_h, state_c] # define training decoder decoder_inputs = Input(shape=(None, n_output)) decoder_lstm = LSTM(n_units, return_sequences=True, return_state=True) decoder_outputs, _, _ = decoder_lstm(decoder_inputs, initial_state=encoder_states) decoder_dense = Dense(n_output, activation='softmax') decoder_outputs = decoder_dense(decoder_outputs) model = Model([encoder_inputs, decoder_inputs], decoder_outputs) # define inference encoder encoder_model = Model(encoder_inputs, encoder_states) # define inference decoder decoder_state_input_h = Input(shape=(n_units,)) decoder_state_input_c = Input(shape=(n_units,)) decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c] decoder_outputs, state_h, state_c = decoder_lstm(decoder_inputs, initial_state=decoder_states_inputs) decoder_states = [state_h, state_c] decoder_outputs = decoder_dense(decoder_outputs) decoder_model = Model([decoder_inputs] + decoder_states_inputs, [decoder_outputs] + decoder_states) # return all models return model, encoder_model, decoder_model ``` 該函數有3個參數,如下所示: * **n_input** :輸入序列的基數,例如每個時間步長的要素,單詞或字符數。 * **n_output** :輸出序列的基數,例如每個時間步長的要素,單詞或字符數。 * **n_units** :在編碼器和解碼器模型中創建的單元數,例如128或256。 然后該函數創建并返回3個模型,如下所示: * **train** :在給定源,目標和移位目標序列的情況下可以訓練的模型。 * **inference_encoder** :在對新源序列進行預測時使用的編碼器模型。 * **inference_decoder** 解碼器模型在對新源序列進行預測時使用。 在給定源序列和目標序列的情況下訓練模型,其中模型將源序列和??移位版本的目標序列作為輸入并預測整個目標序列。 例如,一個源序列可以是[1,2,3]和靶序列[4,5,6]。訓練期間模型的輸入和輸出將是: ```py Input1: ['1', '2', '3'] Input2: ['_', '4', '5'] Output: ['4', '5', '6'] ``` 當為新源序列生成目標序列時,該模型旨在被遞歸地調用。 對源序列進行編碼,并且使用諸如“_”的“序列開始”字符一次一個元素地生成目標序列以開始該過程。因此,在上述情況下,在訓練期間會出現以下輸入輸出對: ```py t, Input1, Input2, Output 1, ['1', '2', '3'], '_', '4' 2, ['1', '2', '3'], '4', '5' 3, ['1', '2', '3'], '5', '6' ``` 在這里,您可以看到如何使用模型的遞歸使用來構建輸出序列。 在預測期間, _inference_encoder_ 模型用于編碼輸入序列,其返回用于初始化 _inference_decoder_ 模型的狀態。從那時起, _inference_decoder_ 模型用于逐步生成預測。 在訓練模型以生成給定源序列的目標序列之后,可以使用下面名為 _predict_sequence()_的函數。 ```py # generate target given source sequence def predict_sequence(infenc, infdec, source, n_steps, cardinality): # encode state = infenc.predict(source) # start of sequence input target_seq = array([0.0 for _ in range(cardinality)]).reshape(1, 1, cardinality) # collect predictions output = list() for t in range(n_steps): # predict next char yhat, h, c = infdec.predict([target_seq] + state) # store prediction output.append(yhat[0,0,:]) # update state state = [h, c] # update target sequence target_seq = yhat return array(output) ``` 該函數有5個參數如下: * **infenc** :在對新源序列進行預測時使用的編碼器模型。 * **infdec** :在對新源序列進行預測時使用解碼器模型。 * **source** :編碼的源序列。 * **n_steps** :目標序列中的時間步數。 * **基數**:輸出序列的基數,例如每個時間步的要素,單詞或字符的數量。 然后該函數返回包含目標序列的列表。 ## 可擴展的序列到序列問題 在本節中,我們定義了一個人為的,可擴展的序列到序列預測問題。 源序列是一系列隨機生成的整數值,例如[20,36,40,10,34,28],目標序列是輸入序列的反向預定義子集,例如前3個元素以相反的順序[40,36,20]。 源序列的長度是可配置的;輸入和輸出序列的基數以及目標序列的長度也是如此。 我們將使用6個元素的源序列,基數為50,以及3個元素的目標序列。 下面是一些更具體的例子。 ```py Source, Target [13, 28, 18, 7, 9, 5] [18, 28, 13] [29, 44, 38, 15, 26, 22] [38, 44, 29] [27, 40, 31, 29, 32, 1] [31, 40, 27] ... ``` 我們鼓勵您探索更大,更復雜的變體。在下面的評論中發布您的發現。 讓我們首先定義一個函數來生成一系列隨機整數。 我們將使用0的值作為填充或序列字符的開頭,因此它是保留的,我們不能在源序列中使用它。為實現此目的,我們將為配置的基數添加1,以確保單熱編碼足夠大(例如,值1映射到索引1中的'1'值)。 例如: ```py n_features = 50 + 1 ``` 我們可以使用 _randint()_ python函數生成1到1范圍內的隨機整數 - 減去問題基數的大小。下面的 _generate_sequence()_生成一系列隨機整數。 ```py # generate a sequence of random integers def generate_sequence(length, n_unique): return [randint(1, n_unique-1) for _ in range(length)] ``` 接下來,我們需要在給定源序列的情況下創建相應的輸出序列。 為了簡單起見,我們將選擇源序列的前n個元素作為目標序列并反轉它們。 ```py # define target sequence target = source[:n_out] target.reverse() ``` 我們還需要一個版本的輸出序列向前移動一個步驟,我們可以將其用作到目前為止生成的模擬目標,包括第一個時間步驟中序列值的開始。我們可以直接從目標序列創建它。 ```py # create padded input target sequence target_in = [0] + target[:-1] ``` 現在已經定義了所有序列,我們可以對它們進行一次熱編碼,即將它們轉換成二進制向量序列。我們可以使用內置 _to_categorical()_函數的Keras來實現這一點。 我們可以將所有這些放入名為 _get_dataset()_的函數中,該函數將生成特定數量的序列,我們可以使用它來訓練模型。 ```py # prepare data for the LSTM def get_dataset(n_in, n_out, cardinality, n_samples): X1, X2, y = list(), list(), list() for _ in range(n_samples): # generate source sequence source = generate_sequence(n_in, cardinality) # define target sequence target = source[:n_out] target.reverse() # create padded input target sequence target_in = [0] + target[:-1] # encode src_encoded = to_categorical([source], num_classes=cardinality) tar_encoded = to_categorical([target], num_classes=cardinality) tar2_encoded = to_categorical([target_in], num_classes=cardinality) # store X1.append(src_encoded) X2.append(tar2_encoded) y.append(tar_encoded) return array(X1), array(X2), array(y) ``` 最后,我們需要能夠解碼一個熱門的編碼序列,使其再次可讀。 這對于打印所生成的靶序列以及容易地比較完整預測的靶序列是否與預期的靶序列匹配是必需的。 _one_hot_decode()_函數將解碼編碼序列。 ```py # decode a one hot encoded string def one_hot_decode(encoded_seq): return [argmax(vector) for vector in encoded_seq] ``` 我們可以將所有這些結合在一起并測試這些功能。 下面列出了一個完整的工作示例。 ```py from random import randint from numpy import array from numpy import argmax from keras.utils import to_categorical # generate a sequence of random integers def generate_sequence(length, n_unique): return [randint(1, n_unique-1) for _ in range(length)] # prepare data for the LSTM def get_dataset(n_in, n_out, cardinality, n_samples): X1, X2, y = list(), list(), list() for _ in range(n_samples): # generate source sequence source = generate_sequence(n_in, cardinality) # define target sequence target = source[:n_out] target.reverse() # create padded input target sequence target_in = [0] + target[:-1] # encode src_encoded = to_categorical([source], num_classes=cardinality) tar_encoded = to_categorical([target], num_classes=cardinality) tar2_encoded = to_categorical([target_in], num_classes=cardinality) # store X1.append(src_encoded) X2.append(tar2_encoded) y.append(tar_encoded) return array(X1), array(X2), array(y) # decode a one hot encoded string def one_hot_decode(encoded_seq): return [argmax(vector) for vector in encoded_seq] # configure problem n_features = 50 + 1 n_steps_in = 6 n_steps_out = 3 # generate a single source and target sequence X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 1) print(X1.shape, X2.shape, y.shape) print('X1=%s, X2=%s, y=%s' % (one_hot_decode(X1[0]), one_hot_decode(X2[0]), one_hot_decode(y[0]))) ``` 首先運行示例打印生成的數據集的形狀,確保訓練模型所需的3D形狀符合我們的期望。 然后將生成的序列解碼并打印到屏幕,證明源和目標序列的準備符合我們的意圖并且解碼操作正在起作用。 ```py (1, 6, 51) (1, 3, 51) (1, 3, 51) X1=[32, 16, 12, 34, 25, 24], X2=[0, 12, 16], y=[12, 16, 32] ``` 我們現在準備為這個序列到序列預測問題開發一個模型。 ## 用于序列預測的編碼器 - 解碼器LSTM 在本節中,我們將第一部分中開發的編碼器 - 解碼器LSTM模型應用于第二部分中開發的序列到序列預測問題。 第一步是配置問題。 ```py # configure problem n_features = 50 + 1 n_steps_in = 6 n_steps_out = 3 ``` 接下來,我們必須定義模型并編譯訓練模型。 ```py # define model train, infenc, infdec = define_models(n_features, n_features, 128) train.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc']) ``` 接下來,我們可以生成100,000個示例的訓練數據集并訓練模型。 ```py # generate training dataset X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 100000) print(X1.shape,X2.shape,y.shape) # train model train.fit([X1, X2], y, epochs=1) ``` 一旦模型被訓練,我們就可以對其進行評估。我們將通過對100個源序列進行預測并計算正確預測的目標序列的數量來實現此目的。我們將在解碼序列上使用numpy _array_equal()_函數來檢查是否相等。 ```py # evaluate LSTM total, correct = 100, 0 for _ in range(total): X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 1) target = predict_sequence(infenc, infdec, X1, n_steps_out, n_features) if array_equal(one_hot_decode(y[0]), one_hot_decode(target)): correct += 1 print('Accuracy: %.2f%%' % (float(correct)/float(total)*100.0)) ``` 最后,我們將生成一些預測并打印解碼的源,目標和預測的目標序列,以了解模型是否按預期工作。 將所有這些元素放在一起,下面列出了完整的代碼示例。 ```py from random import randint from numpy import array from numpy import argmax from numpy import array_equal from keras.utils import to_categorical from keras.models import Model from keras.layers import Input from keras.layers import LSTM from keras.layers import Dense # generate a sequence of random integers def generate_sequence(length, n_unique): return [randint(1, n_unique-1) for _ in range(length)] # prepare data for the LSTM def get_dataset(n_in, n_out, cardinality, n_samples): X1, X2, y = list(), list(), list() for _ in range(n_samples): # generate source sequence source = generate_sequence(n_in, cardinality) # define padded target sequence target = source[:n_out] target.reverse() # create padded input target sequence target_in = [0] + target[:-1] # encode src_encoded = to_categorical([source], num_classes=cardinality) tar_encoded = to_categorical([target], num_classes=cardinality) tar2_encoded = to_categorical([target_in], num_classes=cardinality) # store X1.append(src_encoded) X2.append(tar2_encoded) y.append(tar_encoded) return array(X1), array(X2), array(y) # returns train, inference_encoder and inference_decoder models def define_models(n_input, n_output, n_units): # define training encoder encoder_inputs = Input(shape=(None, n_input)) encoder = LSTM(n_units, return_state=True) encoder_outputs, state_h, state_c = encoder(encoder_inputs) encoder_states = [state_h, state_c] # define training decoder decoder_inputs = Input(shape=(None, n_output)) decoder_lstm = LSTM(n_units, return_sequences=True, return_state=True) decoder_outputs, _, _ = decoder_lstm(decoder_inputs, initial_state=encoder_states) decoder_dense = Dense(n_output, activation='softmax') decoder_outputs = decoder_dense(decoder_outputs) model = Model([encoder_inputs, decoder_inputs], decoder_outputs) # define inference encoder encoder_model = Model(encoder_inputs, encoder_states) # define inference decoder decoder_state_input_h = Input(shape=(n_units,)) decoder_state_input_c = Input(shape=(n_units,)) decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c] decoder_outputs, state_h, state_c = decoder_lstm(decoder_inputs, initial_state=decoder_states_inputs) decoder_states = [state_h, state_c] decoder_outputs = decoder_dense(decoder_outputs) decoder_model = Model([decoder_inputs] + decoder_states_inputs, [decoder_outputs] + decoder_states) # return all models return model, encoder_model, decoder_model # generate target given source sequence def predict_sequence(infenc, infdec, source, n_steps, cardinality): # encode state = infenc.predict(source) # start of sequence input target_seq = array([0.0 for _ in range(cardinality)]).reshape(1, 1, cardinality) # collect predictions output = list() for t in range(n_steps): # predict next char yhat, h, c = infdec.predict([target_seq] + state) # store prediction output.append(yhat[0,0,:]) # update state state = [h, c] # update target sequence target_seq = yhat return array(output) # decode a one hot encoded string def one_hot_decode(encoded_seq): return [argmax(vector) for vector in encoded_seq] # configure problem n_features = 50 + 1 n_steps_in = 6 n_steps_out = 3 # define model train, infenc, infdec = define_models(n_features, n_features, 128) train.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc']) # generate training dataset X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 100000) print(X1.shape,X2.shape,y.shape) # train model train.fit([X1, X2], y, epochs=1) # evaluate LSTM total, correct = 100, 0 for _ in range(total): X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 1) target = predict_sequence(infenc, infdec, X1, n_steps_out, n_features) if array_equal(one_hot_decode(y[0]), one_hot_decode(target)): correct += 1 print('Accuracy: %.2f%%' % (float(correct)/float(total)*100.0)) # spot check some examples for _ in range(10): X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 1) target = predict_sequence(infenc, infdec, X1, n_steps_out, n_features) print('X=%s y=%s, yhat=%s' % (one_hot_decode(X1[0]), one_hot_decode(y[0]), one_hot_decode(target))) ``` 首先運行該示例將打印準備好的數據集的形狀。 ```py (100000, 6, 51) (100000, 3, 51) (100000, 3, 51) ``` 接下來,該模型是合適的。您應該看到一個進度條,并且在現代多核CPU上運行應該不到一分鐘。 ```py 100000/100000 [==============================] - 50s - loss: 0.6344 - acc: 0.7968 ``` 接下來,評估模型并打印精度。我們可以看到該模型在新隨機生成的示例上實現了100%的準確性。 ```py Accuracy: 100.00% ``` 最后,生成10個新示例并預測目標序列。同樣,我們可以看到模型在每種情況下正確地預測輸出序列,并且期望值與源序列的反向前3個元素匹配。 ```py X=[22, 17, 23, 5, 29, 11] y=[23, 17, 22], yhat=[23, 17, 22] X=[28, 2, 46, 12, 21, 6] y=[46, 2, 28], yhat=[46, 2, 28] X=[12, 20, 45, 28, 18, 42] y=[45, 20, 12], yhat=[45, 20, 12] X=[3, 43, 45, 4, 33, 27] y=[45, 43, 3], yhat=[45, 43, 3] X=[34, 50, 21, 20, 11, 6] y=[21, 50, 34], yhat=[21, 50, 34] X=[47, 42, 14, 2, 31, 6] y=[14, 42, 47], yhat=[14, 42, 47] X=[20, 24, 34, 31, 37, 25] y=[34, 24, 20], yhat=[34, 24, 20] X=[4, 35, 15, 14, 47, 33] y=[15, 35, 4], yhat=[15, 35, 4] X=[20, 28, 21, 39, 5, 25] y=[21, 28, 20], yhat=[21, 28, 20] X=[50, 38, 17, 25, 31, 48] y=[17, 38, 50], yhat=[17, 38, 50] ``` 您現在有一個編碼器 - 解碼器LSTM模型的模板,您可以將其應用于您自己的序列到序列預測問題。 ## 進一步閱讀 如果您希望深入了解,本節將提供有關該主題的更多資源。 ### 相關文章 * [如何使用Anaconda設置用于機器學習和深度學習的Python環境](https://machinelearningmastery.com/setup-python-environment-machine-learning-deep-learning-anaconda/) * [如何定義Keras神經機器翻譯的編碼器 - 解碼器序列 - 序列模型](https://machinelearningmastery.com/define-encoder-decoder-sequence-sequence-model-neural-machine-translation-keras/) * [了解Keras中LSTM的返回序列和返回狀態之間的差異](https://machinelearningmastery.com/return-sequences-and-return-states-for-lstms-in-keras/) * [如何使用Keras功能API進行深度學習](https://machinelearningmastery.com/keras-functional-api-deep-learning/) ### Keras資源 * [Keras](https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html) 中序列到序列學習的十分鐘介紹 * [Keras seq2seq代碼示例(lstm_seq2seq)](https://github.com/fchollet/keras/blob/master/examples/lstm_seq2seq.py) * [Keras功能API](https://keras.io/getting-started/functional-api-guide/) * [Keras的LSTM API](https://keras.io/layers/recurrent/#lstm) ## 摘要 在本教程中,您了解了如何使用Keras為序列到序列預測問題開發編碼器 - 解碼器循環神經網絡。 具體來說,你學到了: * 如何在Keras中正確定義復雜的編碼器 - 解碼器模型以進行序列到序列預測。 * 如何定義可用于評估編碼器 - 解碼器LSTM模型的人為但可擴展的序列到序列預測問題。 * 如何在Keras中應用編碼器 - 解碼器LSTM模型來解決可伸縮的整數序列到序列預測問題。 你有任何問題嗎? 在下面的評論中提出您的問題,我會盡力回答。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看