• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 基于機器學習的啟動耗時自動化測試方案

    發表于:2019-08-16來源:閃念基因作者:閃念基因點擊數: 標簽:機器學習
    而影響用戶體驗最重要的一個指標就是啟動耗時,特別是拉新的時候,關于如何測量啟動耗時,一般有兩個方向:一是通過技術埋點,但基于技術埋點記錄數據很難衡量用戶真實體感(線

    當一個應用的用戶越來越多,業務越來越復雜,性能問題就會突顯,特別是在低端機上的用戶感受尤為明顯,甚至會影響到應用的用戶活躍度、停留時長等重要指標,提升應用在中低端機上的性能迫在眉睫。如何來對研發同學的優化做出合理的評測我們需要思考下面兩點:

    要避免“運動式”性能優化, 有不少團隊在投入了大量時間和精力對應用進行專項治理之后,由于缺少常態化的管控和治理手段,最終導致性能震蕩式波動惡化;

    線上的埋點日志數據不能完全反應用戶對應用的真實體驗和感受;

    而影響用戶體驗最重要的一個指標就是啟動耗時,特別是拉新的時候,關于如何測量啟動耗時,一般有兩個方向:一是通過技術埋點,但基于技術埋點記錄數據很難衡量用戶真實體感(線上統計數據好?真實體感卻差?),而且也無法基于技術埋點獲取競品數據;另一個是通過錄屏分幀測試,但是人工錄屏逐幀分析會有人為感知誤差(結束位邊界認知不一致),而且人工性能專項測試持續交付ROI不高,比如錄制10次,抽取關鍵幀取平均值,差不多要花費半個多小時,采樣次數越多,耗時越久。由于最近一段時間在看機器學習的書,所以在想能不能拿這個案例來實踐一下。

    在此之前我也調研了一下業內已有的類似方案:有通過OCR文字識別的、也有通過圖像對比的,其中圖像對比的方案如果是整圖對比,視頻啟動過程中的廣告、首頁海報是變化的,這樣無法準確識別;另外如果是部分對比,那幺app完整啟動后第一屏不完全展示的地方,每次不一定在同一處,于是我參考了各種方案后,結合自己的想法,就把整個方案實現了一遍,接下來詳細介紹一下此方案。

    整體流程

    階段一主要是采集數據,將視頻轉換為圖片,生成訓練數據和測試數據

    階段二主要是訓練模型和質量評估

    階段三主要是通過訓練好的模型進行預測并計算啟動時間

    環境準備

    由于整個方案我是通過Python實現的,所以本地需要安裝好Python環境,這里我使用的是Mac電腦所以默認帶的Python環境,但如果要用到Python3需要自己升級,另外要安裝pip工具:

    brew install pip3

    安裝scikit-learn,一個簡單的機器學習框架,以及依賴的科學計算軟件包numpy和算法庫scipy:

    pip3 install scikit-learn
    pip3 install numpy
    pip3 install scipy

    圖片處理庫OpenCV和imutils:

    pip3 install opencv-contrib-python
    pip3 install imutils

    對視頻文件進行分幀處理的ffmpeg:

    brew install ffmpeg

    安裝airtest框架(網易的一個跨平臺的UI自動化框架):

    pip3 install -U airtest

    安裝poco框架(網易的一個跨平臺的UI自動化框架):

    pip3 install pocoui

    注意:需要將Android手機開發者選項中的觸摸反饋開關打開,這樣就可以準確識別出點擊應用icon的時刻。

    階段一

    首次安裝

    由于應用第一次安裝會有各種權限彈框,為了避免影響測試準確性,我們需要把第一次安裝時候的彈框點掉,然后殺掉應用重新啟動計算冷啟動時間。

    另外要模擬用戶真實體感,首先要模擬用戶真實的點擊應用啟動的過程,這時候不能通過adb直接喚起應用,我是通過poco框架來實現點擊桌面應用icon的。

    poco = AndroidUiautomationPoco()
    poco.device.wake()
    poco(text='應用名字').click()
    poco(text='下一步').click()
    poco(text='允許').click()
    poco(text='允許').click()
    poco(text='允許').click()
    os.system("adb shell am force-stop {}".format(package_name))

    啟動錄屏

    用adb命令開啟錄屏服務,—time-limit 20 表示錄屏20秒,一般情況下20秒啟動加首頁基本能完成,如果是在低端機上可以適當延長時間。

    錄屏通過單獨線程啟動。

    subprocess.Popen("adb shell screenrecord  --time-limit 20 /sdcard/sample.mp4", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    啟動應用

    測試前對被測應用進行安裝,然后在點擊完權限彈框后,殺掉進程重新點擊桌面icon啟動應用。

    os.system("adb install -r {}".format(apk_path))
    poco(text="應用名字").click()

    等錄屏結束后殺掉進程,然后重復上面的啟動過程,根據采樣率決定重復幾次。

    os.system("adb shell am force-stop {}".format(package_name))

    視頻分幀

    將錄制好的視頻從手機中拉取到本地,然后通過ffmpeg進行分幀處理。

    os.system("adb pull /sdcard/sample.mp4 {}".format(video_local_path))
    os.system("ffmpeg -i {} -r 60 {}%d.jpeg".format(video_local_path, test_path))
    -r 指定抽取的幀率,即從視頻中每秒鐘抽取圖片的數量。60代表每秒抽取60幀。

    提取訓練集和測試集數據

    我們一般把數據按照80%和20%的比例分為訓練集和測試集,這里我們可以錄制10組數據,把其中8組作為訓練集,2組作為測試集。

    階段二

    人工標注訓練集數據

    由于我們是通過圖片分類算法來對啟動各個階段進行識別的,所以首先要定義啟動的階段都有哪些,這里我分為5個階段:

    0_desk:桌面階段

    1_start:點擊icon圖標的階段

    2_splash:閃屏頁出現的階段

    3_loading:首頁加載的階段

    4_stable:首頁渲染穩定的階段

    這五個階段的圖片如下:

    由于應用還會有廣告頁、業務彈框、首頁動態變化等,這些暫時先忽略,不影響整體的測試思路。

    特征提取與描述子生成

    這里選擇SIFT特征,SIFT特征具有放縮、旋轉、光照不變性,同時兼有對幾何畸變,圖像幾何變形的一定程度的魯棒性,使用Python OpenCV擴展模塊中的SIFT特征提取接口,就可以提取圖像的SIFT特征點與描述子。

    詞袋生成

    詞袋生成,是基于描述子數據的基礎上,生成一系列的向量數據,最常見就是首先通過K-Means實現對描述子數據的聚類分析,一般會分成100個聚類、得到每個聚類的中心數據,就生成了100 詞袋,根據每個描述子到這些聚類中心的距離,決定了它屬于哪個聚類,這樣就生成了它的直方圖表示數據。

    SVM分類訓練與模型生成

    使用SVM進行數據的分類訓練,得到輸出模型,這里通過sklearn的線性SVM訓練實現了分類模型訓練與導出。

    import cv2
    import imutils
    import numpy as np
    import os
    from sklearn.svm import LinearSVC
    from sklearn.externals import joblib
    from scipy.cluster.vq import *
    from sklearn.preprocessing import StandardScaler
    # Get the training classes names and store them in a list
    train_path = "dataset/train/"
    training_names = os.listdir(train_path)
    # Get all the path to the images and save them in a list
    # image_paths and the corresponding label in image_paths
    image_paths = []
    image_classes = []
    class_id = 0
    for training_name in training_names:
        dir = os.path.join(train_path, training_name)
        class_path = imutils.imlist(dir)
        image_paths += class_path
        image_classes += [class_id] * len(class_path)
        class_id += 1
    # 創建SIFT特征提取器
    sift = cv2.xfeatures2d.SIFT_create()
    # 特征提取與描述子生成
    des_list = []
    for image_path in image_paths:
        im = cv2.imread(image_path)
        im = cv2.resize(im, (300, 300))
        kpts = sift.detect(im)
        kpts, des = sift.compute(im, kpts)
        des_list.append((image_path, des))
        print("image file path : ", image_path)
    # 描述子向量
    descriptors = des_list[0][1]
    for image_path, descriptor in des_list[1:]:
        descriptors = np.vstack((descriptors, descriptor))
    # 100 聚類 K-Means
    k = 100
    voc, variance = kmeans(descriptors, k, 1)
    # 生成特征直方圖
    im_features = np.zeros((len(image_paths), k), "float32")
    for i in range(len(image_paths)):
        words, distance = vq(des_list[i][1], voc)
        for w in words:
            im_features[i][w] += 1
    # 實現動詞詞頻與出現頻率統計
    nbr_occurences = np.sum((im_features > 0) * 1, axis=0)
    idf = np.array(np.log((1.0 * len(image_paths) + 1) / (1.0 * nbr_occurences + 1)), 'float32')
    # 尺度化
    stdSlr = StandardScaler().fit(im_features)
    im_features = stdSlr.transform(im_features)
    # Train the Linear SVM
    clf = LinearSVC()
    clf.fit(im_features, np.array(image_classes))
    # Save the SVM
    print("training and save model...")
    joblib.dump((clf, training_names, stdSlr, k, voc), "startup.pkl", compress=3)

    預測驗證

    加載預先訓練好的模型,使用模型在測試集上進行數據預測,測試結果表明,對于啟動階段的圖像分類可以獲得比較好的效果。

    下面是預測方法的代碼實現:

    import cv2 as cv
    import numpy as np
    from imutils import paths
    from scipy.cluster.vq import *
    from sklearn.externals import joblib
    def predict_image(image_path, pkl):
        # Load the classifier, class names, scaler, number of clusters and vocabulary
        clf, classes_names, stdSlr, k, voc = joblib.load("eleme.pkl")
        # Create feature extraction and keypoint detector objects
        sift = cv.xfeatures2d.SIFT_create()
        # List where all the descriptors are stored
        des_list = []
        im = cv.imread(image_path, cv.IMREAD_GRAYSCALE)
        im = cv.resize(im, (300, 300))
        kpts = sift.detect(im)
        kpts, des = sift.compute(im, kpts)
        des_list.append((image_path, des))
        descriptors = des_list[0][1]
        for image_path, descriptor in des_list[0:]:
            descriptors = np.vstack((descriptors, descriptor))
        test_features = np.zeros((1, k), "float32")
        words, distance = vq(des_list[0][1], voc)
        for w in words:
            test_features[0][w] += 1
        # Perform Tf-Idf vectorization
        nbr_occurences = np.sum((test_features > 0) * 1, axis=0)
        idf = np.array(np.log((1.0 + 1) / (1.0 * nbr_occurences + 1)), 'float32')
        # Scale the features
        test_features = stdSlr.transform(test_features)
        # Perform the predictions
        predictions = [classes_names[i] for i in clf.predict(test_features)]
        return predictions

    階段三

    采集新的啟動視頻

    和階段1采用的方式一樣。

    用模型進行預測

    和階段2測試模型的做法一樣。

    計算啟動時間

    根據預測結果,確定點擊應用icon階段的圖片和首頁渲染穩定之后的圖片,獲取兩個圖片直接的幀數差值,如果前面以60幀抽取圖片,那幺總耗時 = 幀數差值 * 1/60,具體計算這部分的代碼實現如下:

    from airtest.core.api import *
    from dingtalkchatbot.chatbot import DingtalkChatbot
    from poco.drivers.android.uiautomation import AndroidUiautomationPoco
    webhook = 'https://oapi.dingtalk.com/robot/send?access_token='
    robot = DingtalkChatbot(webhook)
    def calculate(package_name, apk_path, pkl, device_name, app_name, app_version):
        sample = 'sample/screen.mp4'
        test_path = "dataset/test/"
        if not os.path.isdir('sample/'):
            os.makedirs('sample/')
        if not os.path.isdir(test_path):
            os.makedirs(test_path)
        try:
            os.system("adb uninstall {}".format(package_name))
            os.system("adb install -r {}".format(apk_path))
            poco = AndroidUiautomationPoco()
            poco.device.wake()
            time.sleep(2)
            poco(text='應用名').click()
            poco(text='下一步').click()
            poco(text='允許').click()
            poco(text='允許').click()
            poco(text='允許').click()
            os.system("adb shell am force-stop {}".format(package_name))
            subprocess.Popen("adb shell screenrecord  --time-limit 20 /sdcard/sample.mp4", shell=True,
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
            poco(text="應用名").click()
            time.sleep(20)
            os.system("adb pull /sdcard/sample.mp4 {}".format(sample))
            os.system("adb uninstall {}".format(package_name))
            os.system("ffmpeg -i {} -r 60 {}%d.jpeg".format(sample, test_path))
            image_paths = []
            class_path = list(paths.list_images(test_path))
            image_paths += class_path
            start = []
            stable = []
            for image_path in image_paths:
                predictions = predict_image(image_path, pkl)
                if predictions[0] == '1_start':
                    start += [str(image_path.split('/')[2]).split('.')[0]]
                elif predictions[0] == '4_stable':
                    stable += [str(image_path.split('/')[2]).split('.')[0]]
            start_time = int(sorted(start)[0])
            stable_time = int(sorted(stable)[0])
            print("耗時:%.2f 秒" % ((stable_time - start_time) / 60))
            robot.send_text(
                msg="啟動耗時自動化測試結果:\n被測設備:{}\n被測應用:{}\n被測版本:{}\n".format(device_name, app_name,
                                                                       app_version) + "啟動耗時:%.2f 秒" % (
                            (stable_time - start_time) / 60),
                is_at_all=True)
        except:
            shutil.rmtree(test_path)
            if os.path.exists(sample):
                os.remove(sample)
    
    if __name__ == "__main__":
        calculate("package_name", "app/app-release.apk", "startup.pkl", "小米MIX3", "應用名", "10.1.1")

    持續集成

    根據上面測試方法提供的參數,通過Jenkins配置任務,訓練好模型,將以上三個階段通過Python腳本的形式封裝好,另外再配置好WebHook跟打包平臺關聯好,即可實現自動驗證分析計算最新包的首屏加載耗時。

    效果

    通過人工錄屏,然后用QuickTime分幀查看時間軸,計算出的首屏加載耗時跟這套方案得到的結果誤差基本在100毫秒以內,但這個過程一次取數需要15分鐘左右,而現在這套方案一次取數只需要3分鐘左右,效率明顯提升,還避免了不同人操作采集標準不一致的問題。

    原文轉自:https://flashgene.com/archives/56650.html

    ...
    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>