成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

使用深度學習進行音頻分類的端到端示例和解釋

人工智能 深度學習
聲音分類是音頻深度學習中應用最廣泛的方法之一。它包括學習對聲音進行分類并預測聲音的類別。這類問題可以應用到許多實際場景中,例如,對音樂片段進行分類以識別音樂類型,或通過一組揚聲器對短話語進行分類以根據聲音識別說話人。在本文中,我們將介紹一個簡單的演示應用程序,以便理解用于解決此類音頻分類問題的方法。

 聲音分類是音頻深度學習中應用最廣泛的方法之一。它包括學習對聲音進行分類并預測聲音的類別。這類問題可以應用到許多實際場景中,例如,對音樂片段進行分類以識別音樂類型,或通過一組揚聲器對短話語進行分類以根據聲音識別說話人。

在本文中,我們將介紹一個簡單的演示應用程序,以便理解用于解決此類音頻分類問題的方法。我的目標不僅僅是理解事物是如何運作的,還有它為什么會這樣運作。

音頻分類

就像使用MNIST數據集對手寫數字進行分類被認為是計算機視覺的“Hello World”類型的問題一樣,我們可以將此應用視為音頻深度學習的入門問題。

我們將從聲音文件開始,將它們轉換為聲譜圖,將它們輸入到CNN加線性分類器模型中,并產生關于聲音所屬類別的預測。

有許多合適的數據集可以用于不同類型的聲音。這些數據集包含大量音頻樣本,以及每個樣本的類標簽,根據你試圖解決的問題來識別聲音的類型。

這些類標簽通??梢詮囊纛l樣本文件名的某些部分或文件所在的子文件夾名中獲得。另外,類標簽在單獨的元數據文件中指定,通常為TXT、JSON或CSV格式。

演示-對普通城市聲音進行分類

對于我們的演示,我們將使用Urban Sound 8K數據集,該數據集包含從日常城市生活中錄制的普通聲音的語料庫。這些聲音來自于10個分類,如工程噪音、狗叫聲和汽笛聲。每個聲音樣本都標有它所屬的類。

下載數據集后,我們看到它由兩部分組成:

“Audio”文件夾中的音頻文件:它有10個子文件夾,命名為“fold1”到“fold10”。每個子文件夾包含許多。wav的音頻樣本。例如“fold1/103074 - 7 - 1 - 0. - wav”

“Metadata”文件夾中的元數據:它有一個文件“UrbanSound8K”。它包含關于數據集中每個音頻樣本的信息,如文件名、類標簽、“fold”子文件夾位置等。類標簽是10個類中的每個類從0到9的數字類ID。如。數字0表示空調,1表示汽車喇叭,以此類推。

一般音頻的長度約為4秒。下面是其中一個例子:

數據集創建者的建議是使用10折的交叉驗證,以便計算指標并評估模型的性能。 但是,由于本文的目標主要是作為音頻深度學習示例的演示,而不是獲得最佳指標,因此,我們將忽略分折并將所有樣本簡單地視為一個大型數據集。

準備訓練數據

對于大多數深度學習問題,我們將遵循以下步驟:

這個數據集的數據整理很簡單:

特性(X)是音頻文件路徑

目標標簽(y)是類名

由于數據集已經有一個包含此信息的元數據文件,所以我們可以直接使用它。元數據包含關于每個音頻文件的信息。

由于它是一個CSV文件,我們可以使用Pandas來讀取它。我們可以從元數據中準備特性和標簽數據。

  1. # ----------------------------# Prepare training data from Metadata file# ----------------------------import pandas as pdfrom pathlib import Pathdownload_path = Path.cwd()/'UrbanSound8K'# Read metadata filemetadata_file = download_path/'metadata'/'UrbanSound8K.csv'df = pd.read_csv(metadata_file)df.head()# Construct file path by concatenating fold and file namedf['relative_path'] = '/fold' + df['fold'].astype(str) + '/' + df['slice_file_name'].astype(str)# Take relevant columnsdf = df[['relative_path', 'classID']]df.head() 

我們訓練的需要的信息如下:

當元數據不可用時,掃描音頻文件目錄

有了元數據文件,事情就簡單多了。我們如何為不包含元數據文件的數據集準備數據呢?

許多數據集僅包含安排在文件夾結構中的音頻文件,類標簽可以通過目錄進行派生。為了以這種格式準備我們的培訓數據,我們將做以下工作:

掃描該目錄并生成所有音頻文件路徑的列表。

從每個文件名或父子文件夾的名稱中提取類標簽

將每個類名從文本映射到一個數字類ID

不管有沒有元數據,結果都是一樣的——由音頻文件名列表組成的特性和由類id組成的目標標簽。

音頻預處理:定義變換

這種帶有音頻文件路徑的訓練數據不能直接輸入到模型中。我們必須從文件中加載音頻數據并對其進行處理,使其符合模型所期望的格式。

當我們讀取并加載音頻文件時,所有音頻預處理將在運行時動態完成。這種方法也類似于我們將要處理的圖像文件。由于音頻數據(或圖像數據)可能非常大且占用大量內存,因此我們不希望提前一次將整個數據集全部讀取到內存中。因此,我們在訓練數據中僅保留音頻文件名(或圖像文件名)。。

然后在運行時,當我們一次訓練一批數據時,我們將加載該批次的音頻數據,并通過對音頻進行一系列轉換來對其進行處理。這樣,我們一次只將一批音頻數據保存在內存中。

對于圖像數據,我們可能會有一個轉換管道,在該轉換過程中,我們首先將圖像文件讀取為像素并將其加載。然后,我們可以應用一些圖像處理步驟來調整數據的形狀和大小,將其裁剪為固定大小,然后將其從RGB轉換為灰度(如果需要)。我們可能還會應用一些圖像增強步驟,例如旋轉,翻轉等。

音頻數據的處理非常相似?,F在我們只定義函數,當我們在訓練期間向模型提供數據時,它們將在稍后運行。

讀取文件中的音頻

我們需要做的第一件事是以“ .wav”格式讀取和加載音頻文件。 由于我們在此示例中使用的是Pytorch,因此下面的實現使用torchaudio進行音頻處理,但是librosa也可以正常工作。

  1. import math, randomimport torchimport torchaudiofrom torchaudio import transformsfrom IPython.display import Audioclass AudioUtil():# ---------------------------- # Load an audio file. Return the signal as a tensor and the sample rate # ---------------------------- @staticmethod def open(audio_file): sig, sr = torchaudio.load(audio_file) return (sig, sr) 

轉換成立體聲

一些聲音文件是單聲道(即1個音頻通道),而大多數則是立體聲(即2個音頻通道)。 由于我們的模型期望所有項目都具有相同的尺寸,因此我們將第一個通道復制到第二個通道,從而將單聲道文件轉換為立體聲。

# ----------------------------# Convert the given audio to the desired number of channels # ---------------------------- @staticmethod def rechannel(aud, new_channel): sig, sr = aud if (sig.shape[0] == new_channel): # Nothing to do return aud if (new_channel == 1): # Convert from stereo to mono by selecting only the first channel resig = sig[:1, :] else: # Convert from mono to stereo by duplicating the first channel resig = torch.cat([sig, sig]) return ((resig, sr))

標準化采樣率

一些聲音文件以48000Hz的采樣率采樣,而大多數聲音文件以44100Hz的采樣率采樣。 這意味著對于某些聲音文件,1秒音頻的數組大小為48000,而對于其他聲音文件,其數組大小為44100。 ,我們必須將所有音頻標準化并將其轉換為相同的采樣率,以使所有陣列具有相同的尺寸。

# ----------------------------# Since Resample applies to a single channel, we resample one channel at a time # ---------------------------- @staticmethod def resample(aud, newsr): sig, sr = aud if (sr == newsr): # Nothing to do return aud num_channels = sig.shape[0] # Resample first channel resig = torchaudio.transforms.Resample(sr, newsr)(sig[:1,:]) if (num_channels > 1): # Resample the second channel and merge both channels retwo = torchaudio.transforms.Resample(sr, newsr)(sig[1:,:]) resig = torch.cat([resig, retwo]) return ((resig, newsr))

調整為相同長度

然后,我們將所有音頻樣本的大小調整為具有相同的長度,方法是通過使用靜默填充或通過截斷其長度來延長其持續時間。 我們將該方法添加到AudioUtil類中。

# ----------------------------# Pad (or truncate) the signal to a fixed length 'max_ms' in milliseconds # ---------------------------- @staticmethod def pad_trunc(aud, max_ms): sig, sr = aud num_rows, sig_len = sig.shape max_len = sr//1000 * max_ms if (sig_len > max_len): # Truncate the signal to the given length sig = sig[:,:max_len] elif (sig_len < max_len): # Length of padding to add at the beginning and end of the signal pad_begin_len = random.randint(0, max_len - sig_len) pad_end_len = max_len - sig_len - pad_begin_len # Pad with 0s pad_begin = torch.zeros((num_rows, pad_begin_len)) pad_end = torch.zeros((num_rows, pad_end_len)) sig = torch.cat((pad_begin, sig, pad_end), 1) return (sig, sr)

數據擴充增廣:時移

接下來,我們可以通過應用時間偏移將音頻向左或向右移動隨機量來對原始音頻信號進行數據增廣。 在本文中,我將詳細介紹此技術和其他數據增廣技術。

# ----------------------------# Shifts the signal to the left or right by some percent. Values at the end # are 'wrapped around' to the start of the transformed signal. # ---------------------------- @staticmethod def time_shift(aud, shift_limit): sig,sr = aud _, sig_len = sig.shape shift_amt = int(random.random() * shift_limit * sig_len) return (sig.roll(shift_amt), sr)

梅爾譜圖

我們將增廣后的音頻轉換為梅爾頻譜圖。 它們捕獲了音頻的基本特征,并且通常是將音頻數據輸入到深度學習模型中的最合適方法。

# ----------------------------# Generate a Spectrogram # ---------------------------- @staticmethod def spectro_gram(aud, n_mels=64, n_fft=1024, hop_len=None): sig,sr = aud top_db = 80 # spec has shape [channel, n_mels, time], where channel is mono, stereo etc spec = transforms.MelSpectrogram(sr, n_fft=n_fft, hop_length=hop_len, n_mels=n_mels)(sig) # Convert to decibels spec = transforms.AmplitudeToDB(top_db=top_db)(spec) return (spec)

數據擴充:時間和頻率屏蔽

現在我們可以進行另一輪擴充,這次是在Mel頻譜圖上,而不是在原始音頻上。 我們將使用一種稱為SpecAugment的技術,該技術使用以下兩種方法:

頻率屏蔽-通過在頻譜圖上添加水平條來隨機屏蔽一系列連續頻率。

時間掩碼-與頻率掩碼類似,不同之處在于,我們使用豎線從頻譜圖中隨機地遮擋了時間范圍。

# ----------------------------# Augment the Spectrogram by masking out some sections of it in both the frequency # dimension (ie. horizontal bars) and the time dimension (vertical bars) to prevent # overfitting and to help the model generalise better. The masked sections are # replaced with the mean value. # ---------------------------- @staticmethod def spectro_augment(spec, max_mask_pct=0.1, n_freq_masks=1, n_time_masks=1): _, n_mels, n_steps = spec.shape mask_value = spec.mean() aug_spec = spec freq_mask_param = max_mask_pct * n_mels for _ in range(n_freq_masks): aug_spec = transforms.FrequencyMasking(freq_mask_param)(aug_spec, mask_value) time_mask_param = max_mask_pct * n_steps for _ in range(n_time_masks): aug_spec = transforms.TimeMasking(time_mask_param)(aug_spec, mask_value) return aug_spec

自定義數據加載器

現在,我們已經定義了所有預處理轉換函數,我們將定義一個自定義的Pytorch Dataset對象。

要將數據提供給使用Pytorch的模型,我們需要兩個對象:

一個自定義Dataset對象,該對象使用所有音頻轉換來預處理音頻文件并一次準備一個數據項。

內置的DataLoader對象,該對象使用Dataset對象來獲取單個數據項并將其打包為一批數據。

from torch.utils.data import DataLoader, Dataset, random_splitimport torchaudio# ----------------------------# Sound Dataset# ----------------------------class SoundDS(Dataset):def __init__(self, df, data_path): self.df = df self.data_path = str(data_path) self.duration = 4000 self.sr = 44100 self.channel = 2 self.shift_pct = 0.4 # ---------------------------- # Number of items in dataset # ---------------------------- def __len__(self): return len(self.df) # ---------------------------- # Get i'th item in dataset # ---------------------------- def __getitem__(self, idx): # Absolute file path of the audio file - concatenate the audio directory with # the relative path audio_file = self.data_path + self.df.loc[idx, 'relative_path'] # Get the Class ID class_id = self.df.loc[idx, 'classID'] aud = AudioUtil.open(audio_file) # Some sounds have a higher sample rate, or fewer channels compared to the # majority. So make all sounds have the same number of channels and same # sample rate. Unless the sample rate is the same, the pad_trunc will still # result in arrays of different lengths, even though the sound duration is # the same. reaud = AudioUtil.resample(aud, self.sr) rechan = AudioUtil.rechannel(reaud, self.channel) dur_aud = AudioUtil.pad_trunc(rechan, self.duration) shift_aud = AudioUtil.time_shift(dur_aud, self.shift_pct) sgram = AudioUtil.spectro_gram(shift_aud, n_mels=64, n_fft=1024, hop_len=None) aug_sgram = AudioUtil.spectro_augment(sgram, max_mask_pct=0.1, n_freq_masks=2, n_time_masks=2) return aug_sgram, class_id

使用數據加載器準備一批數據

現在已經定義了我們需要將數據輸入到模型中的所有函數。

我們使用自定義數據集從Pandas中加載特征和標簽,然后以80:20的比例將數據隨機分為訓練和驗證集。 然后,我們使用它們來創建我們的訓練和驗證數據加載器。

from torch.utils.data import random_splitmyds = SoundDS(df, data_path)# Random split of 80:20 between training and validationnum_items = len(myds)num_train = round(num_items * 0.8)num_val = num_items - num_traintrain_ds, val_ds = random_split(myds, [num_train, num_val])# Create training and validation data loaderstrain_dl = torch.utils.data.DataLoader(train_ds, batch_size=16, shuffle=True)val_dl = torch.utils.data.DataLoader(val_ds, batch_size=16, shuffle=False)

當我們開始訓練時,將隨機獲取一批包含音頻文件名列表的輸入,并在每個音頻文件上運行預處理音頻轉換。 它還將獲取一批包含類ID的相應目標Label。 因此,它將一次輸出一批訓練數據,這些數據可以直接作為輸入提供給我們的深度學習模型。

讓我們從音頻文件開始,逐步完成數據轉換的各個步驟:

文件中的音頻被加載到Numpy的數組中(numchannels,numsamples)。大部分音頻以44.1kHz采樣,持續時間約為4秒,從而產生44,100 * 4 = 176,400個采樣。如果音頻具有1個通道,則陣列的形狀將為(1、176,400)。同樣,具有2個通道的4秒鐘持續時間且以48kHz采樣的音頻將具有192,000個采樣,形狀為(2,192,000)。

每種音頻的通道和采樣率不同,因此接下來的兩次轉換會將音頻重新采樣為標準的44.1kHz和標準的2個通道。

某些音頻片段可能大于或小于4秒,因此我們還將音頻持續時間標準化為固定的4秒長度。現在,所有項目的數組都具有相同的形狀(2,176,400)

時移數據擴充功能會隨機將每個音頻樣本向前或向后移動。形狀不變。

擴充后的音頻將轉換為梅爾頻譜圖,其形狀為(numchannels,Mel freqbands,time_steps)=(2,64,344)

SpecAugment數據擴充功能將時間和頻率掩碼隨機應用于梅爾頻譜圖。形狀不變。

最后我們每批得到了兩個張量,一個用于包含梅爾頻譜圖的X特征數據,另一個用于包含數字類ID的y目標標簽。 從每個訓練輪次的訓練數據中隨機選擇批次。

每個批次的形狀為(batchsz,numchannels,Mel freqbands,timesteps)


我們可以將批次中的一項可視化。 我們看到帶有垂直和水平條紋的梅爾頻譜圖顯示了頻率和時間屏蔽數據的擴充。

建立模型

我們剛剛執行的數據處理步驟是我們音頻分類問題中最獨特的方面。 從這里開始,模型和訓練過程與標準圖像分類問題中常用的模型和訓練過程非常相似,并且不特定于音頻深度學習。

由于我們的數據現在由光譜圖圖像組成,因此我們建立了CNN分類架構來對其進行處理。 它具有生成特征圖的四個卷積塊。 然后將數據重新整形為我們需要的格式,以便可以將其輸入到線性分類器層,該層最終輸出針對10個分類的預測。

模型信息:

色彩圖像以形狀(batchsz,numchannels,Mel freqbands,timesteps)輸入模型。(16,2,64,344)。

每個CNN層都應用其濾鏡以提高圖像深度,即通道數。 (16、64、4、22)。

將其合并并展平為(16,64)的形狀,然后輸入到“線性”層。

線性層為每個類別輸出一個預測分數,即(16、10)

import torch.nn.functional as Ffrom torch.nn import init# ----------------------------# Audio Classification Model# ----------------------------class AudioClassifier (nn.Module):# ---------------------------- # Build the model architecture # ---------------------------- def __init__(self): super().__init__() conv_layers = [] # First Convolution Block with Relu and Batch Norm. Use Kaiming Initialization self.conv1 = nn.Conv2d(2, 8, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2)) self.relu1 = nn.ReLU() self.bn1 = nn.BatchNorm2d(8) init.kaiming_normal_(self.conv1.weight, a=0.1) self.conv1.bias.data.zero_() conv_layers += [self.conv1, self.relu1, self.bn1] # Second Convolution Block self.conv2 = nn.Conv2d(8, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)) self.relu2 = nn.ReLU() self.bn2 = nn.BatchNorm2d(16) init.kaiming_normal_(self.conv2.weight, a=0.1) self.conv2.bias.data.zero_() conv_layers += [self.conv2, self.relu2, self.bn2] # Second Convolution Block self.conv3 = nn.Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)) self.relu3 = nn.ReLU() self.bn3 = nn.BatchNorm2d(32) init.kaiming_normal_(self.conv3.weight, a=0.1) self.conv3.bias.data.zero_() conv_layers += [self.conv3, self.relu3, self.bn3] # Second Convolution Block self.conv4 = nn.Conv2d(32, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)) self.relu4 = nn.ReLU() self.bn4 = nn.BatchNorm2d(64) init.kaiming_normal_(self.conv4.weight, a=0.1) self.conv4.bias.data.zero_() conv_layers += [self.conv4, self.relu4, self.bn4] # Linear Classifier self.ap = nn.AdaptiveAvgPool2d(output_size=1) self.lin = nn.Linear(in_features=64, out_features=10) # Wrap the Convolutional Blocks self.conv = nn.Sequential(*conv_layers) # ---------------------------- # Forward pass computations # ---------------------------- def forward(self, x): # Run the convolutional blocks x = self.conv(x) # Adaptive pool and flatten for input to linear layer x = self.ap(x) x = x.view(x.shape[0], -1) # Linear layer x = self.lin(x) # Final output return x# Create the model and put it on the GPU if availablemyModel = AudioClassifier()device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")myModel = myModel.to(device)# Check that it is on Cudanext(myModel.parameters()).device

訓練

現在,我們準備創建訓練循環來訓練模型。

我們定義了優化器,損失函數和學習率的調度計劃的函數,以便隨著訓練的進行而動態地改變我們的學習率,這樣可以使模型收斂的更快。

在每輪訓練完成后。 我們跟蹤一個簡單的準確性指標,該指標衡量正確預測的百分比。

# ----------------------------# Training Loop# ----------------------------def training(model, train_dl, num_epochs):# Loss Function, Optimizer and Scheduler criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(),lr=0.001) scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr=0.001, steps_per_epoch=int(len(train_dl)), epochs=num_epochs, anneal_strategy='linear') # Repeat for each epoch for epoch in range(num_epochs): running_loss = 0.0 correct_prediction = 0 total_prediction = 0 # Repeat for each batch in the training set for i, data in enumerate(train_dl): # Get the input features and target labels, and put them on the GPU inputs, labels = data[0].to(device), data[1].to(device) # Normalize the inputs inputs_m, inputs_s = inputs.mean(), inputs.std() inputs = (inputs - inputs_m) / inputs_s # Zero the parameter gradients optimizer.zero_grad() # forward + backward + optimize outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() scheduler.step() # Keep stats for Loss and Accuracy running_loss += loss.item() # Get the predicted class with the highest score _, prediction = torch.max(outputs,1) # Count of predictions that matched the target label correct_prediction += (prediction == labels).sum().item() total_prediction += prediction.shape[0] #if i % 10 == 0: # print every 10 mini-batches # print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 10)) # Print stats at the end of the epoch num_batches = len(train_dl) avg_loss = running_loss / num_batches acc = correct_prediction/total_prediction print(f'Epoch: {epoch}, Loss: {avg_loss:.2f}, Accuracy: {acc:.2f}') print('Finished Training')num_epochs=2 # Just for demo, adjust this higher.training(myModel, train_dl, num_epochs)

推理

通常,作為訓練循環的一部分,我們還將根據驗證數據評估指標。 所以我們會對原始數據中保留測試數據集(被當作是訓練時看不見的數據)進行推理。 出于本演示的目的,我們將為此目的使用驗證數據。

我們禁用梯度更新并運行一個推理循環。 與模型一起執行前向傳播以獲取預測,但是我們不需要反向傳播和優化。

# ----------------------------# Inference# ----------------------------def inference (model, val_dl):correct_prediction = 0 total_prediction = 0 # Disable gradient updates with torch.no_grad(): for data in val_dl: # Get the input features and target labels, and put them on the GPU inputs, labels = data[0].to(device), data[1].to(device) # Normalize the inputs inputs_m, inputs_s = inputs.mean(), inputs.std() inputs = (inputs - inputs_m) / inputs_s # Get predictions outputs = model(inputs) # Get the predicted class with the highest score _, prediction = torch.max(outputs,1) # Count of predictions that matched the target label correct_prediction += (prediction == labels).sum().item() total_prediction += prediction.shape[0] acc = correct_prediction/total_prediction print(f'Accuracy: {acc:.2f}, Total items: {total_prediction}')# Run inference on trained model with the validation setinference(myModel, val_dl)

結論

現在我們已經看到了聲音分類的端到端示例,它是音頻深度學習中最基礎的問題之一。 這不僅可以用于廣泛的應用中,而且我們在此介紹的許多概念和技術都將與更復雜的音頻問題相關,例如自動語音識別,其中我們從人類語音入手,了解人們在說什么,以及將其轉換為文本。

 

責任編輯:梁菲 來源: 互聯網
相關推薦

2021-03-19 17:59:30

深度學習音頻程序

2023-01-05 16:51:04

機器學習人工智能

2021-09-26 13:50:52

AI 數據強化學習

2024-02-19 16:06:53

人工智能AI聲音克隆Python

2023-08-05 13:08:54

2022-11-07 07:54:05

微服務數據庫網關

2017-03-20 10:42:08

語音識別NervanaNeon

2023-12-08 09:50:44

自動駕駛機器學習數據

2018-05-22 10:30:37

深度學習蘑菇街移動端

2019-08-22 11:16:41

云計算數據中心技術

2016-04-18 18:09:43

2024-03-13 09:39:45

端到端自動駕駛

2017-01-05 20:11:34

大數據技術審計系統

2021-06-30 09:00:00

測試Web軟件

2020-10-26 13:51:11

Kafka數據端到端

2024-06-27 09:50:56

2023-10-10 09:31:35

自動駕駛技術

2009-03-17 09:56:00

802.11n測試無線網絡

2024-02-21 09:14:32

端到端自動駕駛

2022-06-05 21:16:08

機器學習Python
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精品二区 | 久久看精品 | 久久精品二区 | 亚洲成人精品一区 | 久久男人 | 日韩一区二区在线视频 | 国产精品视频97 | 国产精品久久久久久52avav | 一区影院| 大乳boobs巨大吃奶挤奶 | 成人黄色在线 | av资源网站 | 97免费视频在线观看 | 欧美一级片在线看 | 蜜桃官网 | 91视频免费视频 | 爱草视频 | 久久久91精品国产一区二区三区 | 日韩欧美第一页 | 少妇一级淫片aaaaaaaaa | 国产成人精品免费视频大全最热 | 四色成人av永久网址 | 国产一区二区三区四区三区四 | 天天天操 | 精品国产乱码久久久久久影片 | 亚洲精品国产a久久久久久 午夜影院网站 | 欧美一区二区三区四区五区无卡码 | 欧美成人精品一区二区三区 | 久久草视频| 91精品国产高清久久久久久久久 | 草草视频在线观看 | 国产在线视频在线观看 | 一区二区三区欧美 | 日日夜夜草 | 欧美自拍第一页 | 成人在线电影在线观看 | 一区二区三区在线观看免费视频 | 亚洲成人精品国产 | 亚洲久草视频 | 在线视频一区二区三区 | 韩日精品视频 |