xref: /aosp_15_r20/external/executorch/.ci/scripts/test_llava.sh (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1#!/bin/bash
2# Copyright (c) Meta Platforms, Inc. and affiliates.
3# All rights reserved.
4#
5# This source code is licensed under the BSD-style license found in the
6# LICENSE file in the root directory of this source tree.
7
8set -exu
9# shellcheck source=/dev/null
10
11BUILD_TYPE=${1:-Debug}
12TARGET_OS=${2:-Native}
13BUILD_DIR=${3:-cmake-out}
14
15echo "Building with BUILD_TYPE: $BUILD_TYPE, TARGET_OS: $TARGET_OS, BUILD_DIR: $BUILD_DIR"
16
17if [[ -z "${PYTHON_EXECUTABLE:-}" ]]; then
18    PYTHON_EXECUTABLE=python3
19fi
20
21TARGET_OS_lower="$(echo "${TARGET_OS}" | awk '{print tolower($0)}')"
22if [[ "${TARGET_OS_lower}" == "android" ]]; then
23    if [[ -z "${ANDROID_NDK}" ]]; then
24        echo "Set ANDROID_NDK environment variable to build for Android."
25        exit 1
26    fi
27fi
28
29# Number of processes for a parallel build
30NPROC=8
31if hash nproc &> /dev/null; then NPROC=$(nproc); fi
32
33EXECUTORCH_COMMON_CMAKE_ARGS="                      \
34        -DCMAKE_INSTALL_PREFIX=${BUILD_DIR}         \
35        -DCMAKE_BUILD_TYPE=${BUILD_TYPE}            \
36        -DEXECUTORCH_ENABLE_LOGGING=ON              \
37        -DEXECUTORCH_BUILD_EXTENSION_MODULE=ON      \
38        -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=ON \
39        -DEXECUTORCH_BUILD_EXTENSION_TENSOR=ON      \
40        -DEXECUTORCH_BUILD_KERNELS_CUSTOM=ON        \
41        -DEXECUTORCH_BUILD_KERNELS_OPTIMIZED=ON     \
42        -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON     \
43        -DEXECUTORCH_BUILD_XNNPACK=ON               \
44        -DEXECUTORCH_DO_NOT_USE_CXX11_ABI=ON        \
45        -DEXECUTORCH_XNNPACK_SHARED_WORKSPACE=ON"
46
47cmake_install_executorch_libraries() {
48    cmake                               \
49        ${EXECUTORCH_COMMON_CMAKE_ARGS} \
50        -B${BUILD_DIR} .
51
52    cmake --build ${BUILD_DIR} -j${NPROC} --target install --config ${BUILD_TYPE}
53}
54
55cmake_install_executorch_libraries_for_android() {
56    cmake                                                                       \
57        -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
58        -DANDROID_ABI=arm64-v8a                                                 \
59        ${EXECUTORCH_COMMON_CMAKE_ARGS}                                         \
60        -B${BUILD_DIR} .
61
62    cmake --build ${BUILD_DIR} -j${NPROC} --target install --config ${BUILD_TYPE}
63}
64
65
66LLAVA_COMMON_CMAKE_ARGS="                        \
67        -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" \
68        -DCMAKE_INSTALL_PREFIX=${BUILD_DIR}      \
69        -DCMAKE_BUILD_TYPE=${BUILD_TYPE}         \
70        -DEXECUTORCH_BUILD_KERNELS_CUSTOM=ON     \
71        -DEXECUTORCH_BUILD_KERNELS_OPTIMIZED=ON  \
72        -DEXECUTORCH_BUILD_XNNPACK=ON"
73
74cmake_build_llava_runner() {
75    dir=examples/models/llava
76    python_lib=$($PYTHON_EXECUTABLE -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())')
77
78    cmake                                 \
79        ${LLAVA_COMMON_CMAKE_ARGS}        \
80        -DCMAKE_PREFIX_PATH="$python_lib" \
81        -B${BUILD_DIR}/${dir}             \
82        ${dir}
83
84    cmake --build ${BUILD_DIR}/${dir} -j${NPROC} --config ${BUILD_TYPE}
85}
86
87
88cmake_build_llava_runner_for_android() {
89    dir=examples/models/llava
90    python_lib=$($PYTHON_EXECUTABLE -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())')
91
92    cmake                                                                       \
93        -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
94        -DANDROID_ABI=arm64-v8a                                                 \
95        ${LLAVA_COMMON_CMAKE_ARGS}                                              \
96        -DCMAKE_PREFIX_PATH="$python_lib"                                       \
97        -DLLAVA_RUNNER_NO_TORCH_DUMMY_IMAGE=ON                                  \
98        -B${BUILD_DIR}/${dir}                                                   \
99        ${dir}
100
101    cmake --build ${BUILD_DIR}/${dir} -j${NPROC} --config ${BUILD_TYPE}
102}
103
104# only export the one without custom op for now since it's
105export_llava() {
106    echo "Starting to export Llava. This will take about 6 mins"
107    $PYTHON_EXECUTABLE -m executorch.examples.models.llava.export_llava --pte-name llava.pte --with-artifacts
108}
109
110# Download a new image with different size, to test if the model can handle different image sizes
111prepare_image_tensor() {
112    echo "Downloading image"
113    curl -o basketball.jpg https://upload.wikimedia.org/wikipedia/commons/7/73/Chicago_Bulls_and_New_Jersey_Nets%2C_March_28%2C_1991.jpg
114    $PYTHON_EXECUTABLE -m executorch.examples.models.llava.image_util --image-path basketball.jpg --output-path image.pt
115}
116
117run_and_verify() {
118    NOW=$(date +"%H:%M:%S")
119    echo "Starting to run llava runner at ${NOW}"
120    if [[ ! -f "llava.pte" ]]; then
121        echo "Export failed. Abort"
122        exit 1
123    fi
124    if [[ ! -f "image.pt" ]]; then
125        echo "image.pt is missing."
126        exit 1
127    fi
128    if [[ ! -f "tokenizer.bin" ]]; then
129        echo "tokenizer.bin is missing."
130        exit 1
131    fi
132
133
134
135    RUNTIME_ARGS="--model_path=llava.pte    \
136        --tokenizer_path=tokenizer.bin      \
137        --image_path=image.pt               \
138        --prompt=ASSISTANT:                 \
139        --temperature=0                     \
140        --seq_len=650"
141
142    if [[ "${TARGET_OS_lower}" == "android" ]]; then
143        echo "Transfer relevant files to the phone via ADB and run llava_main with following args,"
144        echo "$ llava_main ${RUNTIME_ARGS} "
145        exit 0;
146    fi
147
148    ${BUILD_DIR}/examples/models/llava/llava_main ${RUNTIME_ARGS} > result.txt
149
150    # verify result.txt
151    RESULT=$(cat result.txt)
152    # set the expected prefix to be the same as prompt because there's a bug in sdpa_with_kv_cache that causes <unk> tokens.
153    if [[ "$(uname)" == "Darwin" ]]; then
154        EXPECTED_PREFIX="ASSISTANT: image captures a basketball game in progress, with several players on the court. One of the players is dribbling the ball, while the others are in various"
155    else
156        # set the expected prefix to be the same as prompt because there's a bug in sdpa_with_kv_cache that causes <unk> tokens.
157        EXPECTED_PREFIX="ASSISTANT:"
158    fi
159    if [[ "${RESULT}" == *"${EXPECTED_PREFIX}"* ]]; then
160        echo "Expected result prefix: ${EXPECTED_PREFIX}"
161        echo "Actual result: ${RESULT}"
162        echo "Success"
163        exit 0
164    else
165        echo "Expected result prefix: ${EXPECTED_PREFIX}"
166        echo "Actual result: ${RESULT}"
167        echo "Failure; results not the same"
168        exit 1
169    fi
170}
171
172# Step1. Build stuff
173if [[ "${TARGET_OS_lower}" == "android" ]]; then
174    cmake_install_executorch_libraries_for_android
175    cmake_build_llava_runner_for_android
176elif [[ "${TARGET_OS_lower}" == "native" ]]; then
177    cmake_install_executorch_libraries
178    cmake_build_llava_runner
179else
180    echo "Invalid TARGET_OS ($2): ${TARGET_OS}"
181fi
182
183# Step2. Generate the PTE
184export_llava
185
186# Step3. Run
187prepare_image_tensor
188run_and_verify
189