Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f56833e14 | ||
|
|
65891d74cc | ||
|
|
f957fa3d2a | ||
|
|
c252e03c6b | ||
|
|
e63daba33d | ||
|
|
3959109281 | ||
|
|
e411520407 | ||
|
|
43e829f219 | ||
|
|
7837232631 | ||
|
|
4ccce027b2 | ||
|
|
fa61ea744d | ||
|
|
5e4579c11d | ||
|
|
329571131d | ||
|
|
a48b4a3ade | ||
|
|
b87fe13afd | ||
|
|
e50e1f253d | ||
|
|
c6206fb351 | ||
|
|
639091fbe9 | ||
|
|
9293016c9d | ||
|
|
2efd19978d | ||
|
|
61659ef299 | ||
|
|
9565c7f6bd | ||
|
|
fbce16e02d | ||
|
|
7010bb4dff | ||
|
|
48d3161a8d | ||
|
|
271b594e74 | ||
|
|
885e62ea82 | ||
|
|
0e52afc651 | ||
|
|
27b5f17401 | ||
|
|
dfe6d6c664 | ||
|
|
9be0b91927 | ||
|
|
e7e83ed4d1 | ||
|
|
c5602a676c | ||
|
|
c34730d9b4 | ||
|
|
fdcacc1ebb | ||
|
|
496ec9421e | ||
|
|
05006cd6e1 | ||
|
|
b90b1ee9cf | ||
|
|
2cef4badb8 | ||
|
|
a119a4da9a | ||
|
|
6eefd2d49a | ||
|
|
4ff2c8c74b | ||
|
|
51bd9c8004 | ||
|
|
d0d836ae74 | ||
|
|
a2d83dd0c8 | ||
|
|
cc107714d7 | ||
|
|
37c9860b79 | ||
|
|
ccb6b0ac9d | ||
|
|
df4efe26bd | ||
|
|
860a78e248 | ||
|
|
a0adcfb148 | ||
|
|
3d5fdd7b37 | ||
|
|
3e6c428c27 | ||
|
|
96fcb13fc0 | ||
|
|
3e812460cf | ||
|
|
98916e8256 | ||
|
|
298b11069f | ||
|
|
30a91138f8 | ||
|
|
c6937ba44a | ||
|
|
ca5b1969a8 | ||
|
|
50ff966445 | ||
|
|
88ec9d30b1 | ||
|
|
60abda56e0 | ||
|
|
23fce0bd84 | ||
|
|
7c88c4765c | ||
|
|
1f77545cf8 | ||
|
|
8e9f3a4d9e | ||
|
|
78e15bd4af | ||
|
|
97cf2efe45 | ||
|
|
bda7fab9f2 | ||
|
|
c2e18c86e8 | ||
|
|
c3ad6a13e1 | ||
|
|
ebe9d26a72 | ||
|
|
9fa7f415df | ||
|
|
a23262dfde | ||
|
|
e687913bf1 | ||
|
|
200cb6f2ca | ||
|
|
43a70e819b | ||
|
|
614f8736df | ||
|
|
d96b4152d6 | ||
|
|
8f05f5bc6e | ||
|
|
15d0f82760 | ||
|
|
6888fcb581 | ||
|
|
2aecdd57ca | ||
|
|
11ab095230 | ||
|
|
a3a88fc9b2 | ||
|
|
8823dc48bc | ||
|
|
1ac5a616de | ||
|
|
d939f6e86a | ||
|
|
e72aea796e | ||
|
|
a908436729 | ||
|
|
583a02e29e | ||
|
|
96c3e64057 | ||
|
|
0392273e10 | ||
|
|
bf1a388b44 | ||
|
|
c9005337a8 | ||
|
|
2f0bd31a84 | ||
|
|
bfbb929790 | ||
|
|
689e44c9a8 | ||
|
|
985aedda32 | ||
|
|
3f3610b5cd | ||
|
|
118683de8a | ||
|
|
bcc9c0d0b3 | ||
|
|
5865b5e703 | ||
|
|
edf2cb3846 | ||
|
|
99e17232a4 | ||
|
|
710169df5c | ||
|
|
e4c50f1de5 | ||
|
|
0743a1b3b5 | ||
|
|
34a6fd4e60 | ||
|
|
3c1187ce83 | ||
|
|
20eb674100 | ||
|
|
bc80225336 | ||
|
|
ab7e8d285e | ||
|
|
673dbdda17 | ||
|
|
0249509a30 | ||
|
|
52b67c538b | ||
|
|
20345888a3 | ||
|
|
490c51d963 | ||
|
|
45c46779af | ||
|
|
869d023416 | ||
|
|
e9bc3b6c06 | ||
|
|
b542894fb9 | ||
|
|
5498cc0d67 | ||
|
|
aa2b8e0ca5 | ||
|
|
a14e2b321d | ||
|
|
28ffb6c13d | ||
|
|
b88cc32346 | ||
|
|
f532972d60 | ||
|
|
d5b05f70c6 | ||
|
|
6d6dc1b8ed | ||
|
|
199e675cc7 | ||
|
|
742a7333c3 | ||
|
|
e8eb3791c8 | ||
|
|
aa44e06890 | ||
|
|
6448430dbb | ||
|
|
347710f68f | ||
|
|
59ebdf0bb5 |
@@ -1,4 +1,5 @@
|
||||
build*/
|
||||
docs/
|
||||
test/
|
||||
|
||||
.cache/
|
||||
|
||||
@@ -38,6 +38,10 @@ on:
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
ubuntu-latest-cmake:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -65,7 +69,7 @@ jobs:
|
||||
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.event.inputs.create_release == 'true' }}
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
|
||||
- name: Fetch system info
|
||||
@@ -92,6 +96,123 @@ jobs:
|
||||
path: |
|
||||
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip
|
||||
|
||||
ubuntu-latest-cmake-vulkan:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Dependencies
|
||||
id: depends
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential libvulkan-dev glslc
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DSD_BUILD_SHARED_LIBS=ON -DSD_VULKAN=ON
|
||||
cmake --build . --config Release
|
||||
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
|
||||
- name: Fetch system info
|
||||
id: system-info
|
||||
run: |
|
||||
echo "CPU_ARCH=`uname -m`" >> "$GITHUB_OUTPUT"
|
||||
echo "OS_NAME=`lsb_release -s -i`" >> "$GITHUB_OUTPUT"
|
||||
echo "OS_VERSION=`lsb_release -s -r`" >> "$GITHUB_OUTPUT"
|
||||
echo "OS_TYPE=`uname -s`" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Pack artifacts
|
||||
id: pack_artifacts
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
run: |
|
||||
cp ggml/LICENSE ./build/bin/ggml.txt
|
||||
cp LICENSE ./build/bin/stable-diffusion.cpp.txt
|
||||
zip -j sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-vulkan.zip ./build/bin/*
|
||||
|
||||
- name: Upload artifacts
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-vulkan.zip
|
||||
path: |
|
||||
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-vulkan.zip
|
||||
|
||||
build-and-push-docker-images:
|
||||
name: Build and push container images
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
artifact-metadata: write
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
variant: [musa, sycl, vulkan]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to the container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@v1.3.1
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: build-push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64
|
||||
push: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
file: Dockerfile.${{ matrix.variant }}
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.BRANCH_NAME }}-${{ matrix.variant }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
|
||||
macOS-latest-cmake:
|
||||
runs-on: macos-latest
|
||||
|
||||
@@ -118,7 +239,7 @@ jobs:
|
||||
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.event.inputs.create_release == 'true' }}
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
|
||||
- name: Fetch system info
|
||||
@@ -146,7 +267,7 @@ jobs:
|
||||
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip
|
||||
|
||||
windows-latest-cmake:
|
||||
runs-on: windows-2025
|
||||
runs-on: windows-2022
|
||||
|
||||
env:
|
||||
VULKAN_VERSION: 1.4.328.1
|
||||
@@ -163,10 +284,8 @@ jobs:
|
||||
- build: "avx512"
|
||||
defines: "-DGGML_NATIVE=OFF -DGGML_AVX512=ON -DGGML_AVX=ON -DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON"
|
||||
- build: "cuda12"
|
||||
defines: "-DSD_CUDA=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_CUDA_ARCHITECTURES=90;89;86;80;75"
|
||||
# - build: "rocm5.5"
|
||||
# defines: '-G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DSD_HIPBLAS=ON -DCMAKE_BUILD_TYPE=Release -DAMDGPU_TARGETS="gfx1100;gfx1102;gfx1030" -DSD_BUILD_SHARED_LIBS=ON'
|
||||
- build: 'vulkan'
|
||||
defines: "-DSD_CUDA=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_CUDA_ARCHITECTURES='61;70;75;80;86;89;90;100;120' -DCMAKE_CUDA_FLAGS='-Xcudafe \"--diag_suppress=177\" -Xcudafe \"--diag_suppress=550\"'"
|
||||
- build: "vulkan"
|
||||
defines: "-DSD_VULKAN=ON -DSD_BUILD_SHARED_LIBS=ON"
|
||||
steps:
|
||||
- name: Clone
|
||||
@@ -178,41 +297,32 @@ jobs:
|
||||
- name: Install cuda-toolkit
|
||||
id: cuda-toolkit
|
||||
if: ${{ matrix.build == 'cuda12' }}
|
||||
uses: Jimver/cuda-toolkit@v0.2.19
|
||||
uses: Jimver/cuda-toolkit@v0.2.22
|
||||
with:
|
||||
cuda: "12.6.2"
|
||||
cuda: "12.8.1"
|
||||
method: "network"
|
||||
sub-packages: '["nvcc", "cudart", "cublas", "cublas_dev", "thrust", "visual_studio_integration"]'
|
||||
|
||||
- name: Install rocm-toolkit
|
||||
id: rocm-toolkit
|
||||
if: ${{ matrix.build == 'rocm5.5' }}
|
||||
uses: Cyberhan123/rocm-toolkit@v0.1.0
|
||||
with:
|
||||
rocm: "5.5.0"
|
||||
|
||||
- name: Install Ninja
|
||||
id: install-ninja
|
||||
if: ${{ matrix.build == 'rocm5.5' }}
|
||||
uses: urkle/action-get-ninja@v1
|
||||
with:
|
||||
version: 1.11.1
|
||||
- name: Install Vulkan SDK
|
||||
id: get_vulkan
|
||||
if: ${{ matrix.build == 'vulkan' }} https://sdk.lunarg.com/sdk/download/1.4.328.1/windows/vulkansdk-windows-X64-1.4.328.1.exe
|
||||
if: ${{ matrix.build == 'vulkan' }}
|
||||
run: |
|
||||
curl.exe -o $env:RUNNER_TEMP/VulkanSDK-Installer.exe -L "https://sdk.lunarg.com/sdk/download/${env:VULKAN_VERSION}/windows/vulkansdk-windows-X64-${env:VULKAN_VERSION}.exe"
|
||||
& "$env:RUNNER_TEMP\VulkanSDK-Installer.exe" --accept-licenses --default-answer --confirm-command install
|
||||
Add-Content $env:GITHUB_ENV "VULKAN_SDK=C:\VulkanSDK\${env:VULKAN_VERSION}"
|
||||
Add-Content $env:GITHUB_PATH "C:\VulkanSDK\${env:VULKAN_VERSION}\bin"
|
||||
|
||||
- name: Activate MSVC environment
|
||||
id: msvc_dev_cmd
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. ${{ matrix.defines }}
|
||||
cmake --build . --config Release
|
||||
cmake .. -DCMAKE_CXX_FLAGS='/bigobj' -G Ninja -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_BUILD_TYPE=Release ${{ matrix.defines }}
|
||||
cmake --build .
|
||||
|
||||
- name: Check AVX512F support
|
||||
id: check_avx512f
|
||||
@@ -277,6 +387,104 @@ jobs:
|
||||
path: |
|
||||
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-${{ matrix.build }}-x64.zip
|
||||
|
||||
windows-latest-cmake-hip:
|
||||
runs-on: windows-2022
|
||||
|
||||
env:
|
||||
HIPSDK_INSTALLER_VERSION: "25.Q3"
|
||||
GPU_TARGETS: "gfx1151;gfx1200;gfx1201;gfx1100;gfx1101;gfx1102;gfx1030;gfx1031;gfx1032"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Cache ROCm Installation
|
||||
id: cache-rocm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: C:\Program Files\AMD\ROCm
|
||||
key: rocm-${{ env.HIPSDK_INSTALLER_VERSION }}-${{ runner.os }}
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.16
|
||||
with:
|
||||
key: windows-latest-cmake-hip-${{ env.HIPSDK_INSTALLER_VERSION }}-x64
|
||||
evict-old-files: 1d
|
||||
|
||||
- name: Install ROCm
|
||||
if: steps.cache-rocm.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
write-host "Downloading AMD HIP SDK Installer"
|
||||
Invoke-WebRequest -Uri "https://download.amd.com/developer/eula/rocm-hub/AMD-Software-PRO-Edition-${{ env.HIPSDK_INSTALLER_VERSION }}-WinSvr2022-For-HIP.exe" -OutFile "${env:RUNNER_TEMP}\rocm-install.exe"
|
||||
write-host "Installing AMD HIP SDK"
|
||||
$proc = Start-Process "${env:RUNNER_TEMP}\rocm-install.exe" -ArgumentList '-install' -NoNewWindow -PassThru
|
||||
$completed = $proc.WaitForExit(600000)
|
||||
if (-not $completed) {
|
||||
Write-Error "ROCm installation timed out after 10 minutes. Killing the process"
|
||||
$proc.Kill()
|
||||
exit 1
|
||||
}
|
||||
if ($proc.ExitCode -ne 0) {
|
||||
Write-Error "ROCm installation failed with exit code $($proc.ExitCode)"
|
||||
exit 1
|
||||
}
|
||||
write-host "Completed AMD HIP SDK installation"
|
||||
|
||||
- name: Verify ROCm
|
||||
run: |
|
||||
# Find and test ROCm installation
|
||||
$clangPath = Get-ChildItem 'C:\Program Files\AMD\ROCm\*\bin\clang.exe' | Select-Object -First 1
|
||||
if (-not $clangPath) {
|
||||
Write-Error "ROCm installation not found"
|
||||
exit 1
|
||||
}
|
||||
& $clangPath.FullName --version
|
||||
# Set HIP_PATH environment variable for later steps
|
||||
echo "HIP_PATH=$(Resolve-Path 'C:\Program Files\AMD\ROCm\*\bin\clang.exe' | split-path | split-path)" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
$env:CMAKE_PREFIX_PATH="${env:HIP_PATH}"
|
||||
cmake .. `
|
||||
-G "Unix Makefiles" `
|
||||
-DSD_HIPBLAS=ON `
|
||||
-DSD_BUILD_SHARED_LIBS=ON `
|
||||
-DGGML_NATIVE=OFF `
|
||||
-DCMAKE_C_COMPILER=clang `
|
||||
-DCMAKE_CXX_COMPILER=clang++ `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DGPU_TARGETS="${{ env.GPU_TARGETS }}"
|
||||
cmake --build . --config Release --parallel ${env:NUMBER_OF_PROCESSORS}
|
||||
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
|
||||
- name: Pack artifacts
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
run: |
|
||||
md "build\bin\rocblas\library\"
|
||||
md "build\bin\hipblaslt\library"
|
||||
cp "${env:HIP_PATH}\bin\hipblas.dll" "build\bin\"
|
||||
cp "${env:HIP_PATH}\bin\hipblaslt.dll" "build\bin\"
|
||||
cp "${env:HIP_PATH}\bin\rocblas.dll" "build\bin\"
|
||||
cp "${env:HIP_PATH}\bin\rocblas\library\*" "build\bin\rocblas\library\"
|
||||
cp "${env:HIP_PATH}\bin\hipblaslt\library\*" "build\bin\hipblaslt\library\"
|
||||
7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-x64.zip .\build\bin\*
|
||||
|
||||
- name: Upload artifacts
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-x64.zip
|
||||
path: |
|
||||
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-x64.zip
|
||||
|
||||
release:
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
|
||||
@@ -284,8 +492,11 @@ jobs:
|
||||
|
||||
needs:
|
||||
- ubuntu-latest-cmake
|
||||
- ubuntu-latest-cmake-vulkan
|
||||
- build-and-push-docker-images
|
||||
- macOS-latest-cmake
|
||||
- windows-latest-cmake
|
||||
- windows-latest-cmake-hip
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
|
||||
@@ -8,6 +8,11 @@ if (NOT XCODE AND NOT MSVC AND NOT CMAKE_BUILD_TYPE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
add_compile_definitions(_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
|
||||
endif()
|
||||
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
@@ -87,6 +92,38 @@ file(GLOB SD_LIB_SOURCES
|
||||
"*.hpp"
|
||||
)
|
||||
|
||||
find_program(GIT_EXE NAMES git git.exe NO_CMAKE_FIND_ROOT_PATH)
|
||||
if(GIT_EXE)
|
||||
execute_process(COMMAND ${GIT_EXE} describe --tags --abbrev=7 --dirty=+
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE SDCPP_BUILD_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
execute_process(COMMAND ${GIT_EXE} rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE SDCPP_BUILD_COMMIT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT SDCPP_BUILD_VERSION)
|
||||
set(SDCPP_BUILD_VERSION unknown)
|
||||
endif()
|
||||
message(STATUS "stable-diffusion.cpp version ${SDCPP_BUILD_VERSION}")
|
||||
|
||||
if(NOT SDCPP_BUILD_COMMIT)
|
||||
set(SDCPP_BUILD_COMMIT unknown)
|
||||
endif()
|
||||
message(STATUS "stable-diffusion.cpp commit ${SDCPP_BUILD_COMMIT}")
|
||||
|
||||
set_property(
|
||||
SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/version.cpp
|
||||
APPEND PROPERTY COMPILE_DEFINITIONS
|
||||
SDCPP_BUILD_COMMIT=${SDCPP_BUILD_COMMIT} SDCPP_BUILD_VERSION=${SDCPP_BUILD_VERSION}
|
||||
)
|
||||
|
||||
if(SD_BUILD_SHARED_LIBS)
|
||||
message("-- Build shared library")
|
||||
message(${SD_LIB_SOURCES})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG UBUNTU_VERSION=22.04
|
||||
ARG UBUNTU_VERSION=24.04
|
||||
|
||||
FROM ubuntu:$UBUNTU_VERSION AS build
|
||||
|
||||
@@ -17,6 +17,7 @@ RUN apt-get update && \
|
||||
apt-get install --yes --no-install-recommends libgomp1 && \
|
||||
apt-get clean
|
||||
|
||||
COPY --from=build /sd.cpp/build/bin/sd /sd
|
||||
COPY --from=build /sd.cpp/build/bin/sd-cli /sd-cli
|
||||
COPY --from=build /sd.cpp/build/bin/sd-server /sd-server
|
||||
|
||||
ENTRYPOINT [ "/sd" ]
|
||||
ENTRYPOINT [ "/sd-cli" ]
|
||||
@@ -18,6 +18,7 @@ RUN mkdir build && cd build && \
|
||||
|
||||
FROM mthreads/musa:${MUSA_VERSION}-runtime-ubuntu${UBUNTU_VERSION}-amd64 as runtime
|
||||
|
||||
COPY --from=build /sd.cpp/build/bin/sd /sd
|
||||
COPY --from=build /sd.cpp/build/bin/sd-cli /sd-cli
|
||||
COPY --from=build /sd.cpp/build/bin/sd-server /sd-server
|
||||
|
||||
ENTRYPOINT [ "/sd" ]
|
||||
ENTRYPOINT [ "/sd-cli" ]
|
||||
@@ -14,6 +14,7 @@ RUN mkdir build && cd build && \
|
||||
|
||||
FROM intel/oneapi-basekit:${SYCL_VERSION}-devel-ubuntu24.04 AS runtime
|
||||
|
||||
COPY --from=build /sd.cpp/build/bin/sd /sd
|
||||
COPY --from=build /sd.cpp/build/bin/sd-cli /sd-cli
|
||||
COPY --from=build /sd.cpp/build/bin/sd-server /sd-server
|
||||
|
||||
ENTRYPOINT [ "/sd" ]
|
||||
ENTRYPOINT [ "/sd-cli" ]
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
ARG UBUNTU_VERSION=24.04
|
||||
|
||||
FROM ubuntu:$UBUNTU_VERSION AS build
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends build-essential git cmake libvulkan-dev glslc
|
||||
|
||||
WORKDIR /sd.cpp
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN cmake . -B ./build -DSD_VULKAN=ON
|
||||
RUN cmake --build ./build --config Release --parallel
|
||||
|
||||
FROM ubuntu:$UBUNTU_VERSION AS runtime
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install --yes --no-install-recommends libgomp1 libvulkan1 mesa-vulkan-drivers && \
|
||||
apt-get clean
|
||||
|
||||
COPY --from=build /sd.cpp/build/bin/sd-cli /sd-cli
|
||||
COPY --from=build /sd.cpp/build/bin/sd-server /sd-server
|
||||
|
||||
ENTRYPOINT [ "/sd-cli" ]
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<img src="./assets/cat_with_sd_cpp_42.png" width="360x">
|
||||
<img src="./assets/logo.png" width="360x">
|
||||
</p>
|
||||
|
||||
# stable-diffusion.cpp
|
||||
@@ -15,6 +15,15 @@ API and command-line option may change frequently.***
|
||||
|
||||
## 🔥Important News
|
||||
|
||||
* **2026/01/18** 🚀 stable-diffusion.cpp now supports **FLUX.2-klein**
|
||||
👉 Details: [PR #1193](https://github.com/leejet/stable-diffusion.cpp/pull/1193)
|
||||
|
||||
* **2025/12/01** 🚀 stable-diffusion.cpp now supports **Z-Image**
|
||||
👉 Details: [PR #1020](https://github.com/leejet/stable-diffusion.cpp/pull/1020)
|
||||
|
||||
* **2025/11/30** 🚀 stable-diffusion.cpp now supports **FLUX.2-dev**
|
||||
👉 Details: [PR #1016](https://github.com/leejet/stable-diffusion.cpp/pull/1016)
|
||||
|
||||
* **2025/10/13** 🚀 stable-diffusion.cpp now supports **Qwen-Image-Edit / Qwen-Image-Edit 2509**
|
||||
👉 Details: [PR #877](https://github.com/leejet/stable-diffusion.cpp/pull/877)
|
||||
|
||||
@@ -37,13 +46,16 @@ API and command-line option may change frequently.***
|
||||
- SDXL, [SDXL-Turbo](https://huggingface.co/stabilityai/sdxl-turbo)
|
||||
- [Some SD1.x and SDXL distilled models](./docs/distilled_sd.md)
|
||||
- [SD3/SD3.5](./docs/sd3.md)
|
||||
- [Flux-dev/Flux-schnell](./docs/flux.md)
|
||||
- [FLUX.1-dev/FLUX.1-schnell](./docs/flux.md)
|
||||
- [FLUX.2-dev/FLUX.2-klein](./docs/flux2.md)
|
||||
- [Chroma](./docs/chroma.md)
|
||||
- [Chroma1-Radiance](./docs/chroma_radiance.md)
|
||||
- [Qwen Image](./docs/qwen_image.md)
|
||||
- [Z-Image](./docs/z_image.md)
|
||||
- [Ovis-Image](./docs/ovis_image.md)
|
||||
- Image Edit Models
|
||||
- [FLUX.1-Kontext-dev](./docs/kontext.md)
|
||||
- [Qwen Image Edit/Qwen Image Edit 2509](./docs/qwen_image_edit.md)
|
||||
- [Qwen Image Edit series](./docs/qwen_image_edit.md)
|
||||
- Video Models
|
||||
- [Wan2.1/Wan2.2](./docs/wan.md)
|
||||
- [PhotoMaker](https://github.com/TencentARC/PhotoMaker) support.
|
||||
@@ -61,7 +73,7 @@ API and command-line option may change frequently.***
|
||||
- SYCL
|
||||
- Supported weight formats
|
||||
- Pytorch checkpoint (`.ckpt` or `.pth`)
|
||||
- Safetensors (`./safetensors`)
|
||||
- Safetensors (`.safetensors`)
|
||||
- GGUF (`.gguf`)
|
||||
- Supported platforms
|
||||
- Linux
|
||||
@@ -81,7 +93,9 @@ API and command-line option may change frequently.***
|
||||
- [`DPM++ 2M v2`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/8457)
|
||||
- `DPM++ 2S a`
|
||||
- [`LCM`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/13952)
|
||||
- Cross-platform reproducibility (`--rng cuda`, consistent with the `stable-diffusion-webui GPU RNG`)
|
||||
- Cross-platform reproducibility
|
||||
- `--rng cuda`, default, consistent with the `stable-diffusion-webui GPU RNG`
|
||||
- `--rng cpu`, consistent with the `comfyui RNG`
|
||||
- Embedds generation parameters into png output as webui-compatible text string
|
||||
|
||||
## Quick Start
|
||||
@@ -94,7 +108,7 @@ API and command-line option may change frequently.***
|
||||
### Download model weights
|
||||
|
||||
- download weights(.ckpt or .safetensors or .gguf). For example
|
||||
- Stable Diffusion v1.5 from https://huggingface.co/runwayml/stable-diffusion-v1-5
|
||||
- Stable Diffusion v1.5 from https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5
|
||||
|
||||
```sh
|
||||
curl -L -O https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors
|
||||
@@ -103,7 +117,7 @@ API and command-line option may change frequently.***
|
||||
### Generate an image with just one command
|
||||
|
||||
```sh
|
||||
./bin/sd -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat"
|
||||
./bin/sd-cli -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat"
|
||||
```
|
||||
|
||||
***For detailed command-line arguments, check out [cli doc](./examples/cli/README.md).***
|
||||
@@ -116,12 +130,15 @@ If you want to improve performance or reduce VRAM/RAM usage, please refer to [pe
|
||||
|
||||
- [SD1.x/SD2.x/SDXL](./docs/sd.md)
|
||||
- [SD3/SD3.5](./docs/sd3.md)
|
||||
- [Flux-dev/Flux-schnell](./docs/flux.md)
|
||||
- [FLUX.1-dev/FLUX.1-schnell](./docs/flux.md)
|
||||
- [FLUX.2-dev/FLUX.2-klein](./docs/flux2.md)
|
||||
- [FLUX.1-Kontext-dev](./docs/kontext.md)
|
||||
- [Chroma](./docs/chroma.md)
|
||||
- [🔥Qwen Image](./docs/qwen_image.md)
|
||||
- [🔥Qwen Image Edit/Qwen Image Edit 2509](./docs/qwen_image_edit.md)
|
||||
- [🔥Qwen Image Edit series](./docs/qwen_image_edit.md)
|
||||
- [🔥Wan2.1/Wan2.2](./docs/wan.md)
|
||||
- [🔥Z-Image](./docs/z_image.md)
|
||||
- [Ovis-Image](./docs/ovis_image.md)
|
||||
- [LoRA](./docs/lora.md)
|
||||
- [LCM/LCM-LoRA](./docs/lcm.md)
|
||||
- [Using PhotoMaker to personalize image generation](./docs/photo_maker.md)
|
||||
@@ -129,6 +146,7 @@ If you want to improve performance or reduce VRAM/RAM usage, please refer to [pe
|
||||
- [Using TAESD to faster decoding](./docs/taesd.md)
|
||||
- [Docker](./docs/docker.md)
|
||||
- [Quantization and GGUF](./docs/quantization_and_gguf.md)
|
||||
- [Inference acceleration via caching](./docs/caching.md)
|
||||
|
||||
## Bindings
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 556 KiB |
|
After Width: | Height: | Size: 510 KiB |
|
After Width: | Height: | Size: 455 KiB |
|
After Width: | Height: | Size: 511 KiB |
|
After Width: | Height: | Size: 491 KiB |
|
After Width: | Height: | Size: 464 KiB |
|
After Width: | Height: | Size: 552 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 401 KiB |
|
After Width: | Height: | Size: 450 KiB |
|
After Width: | Height: | Size: 870 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
@@ -0,0 +1,975 @@
|
||||
#ifndef __CACHE_DIT_HPP__
|
||||
#define __CACHE_DIT_HPP__
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "ggml_extend.hpp"
|
||||
|
||||
struct DBCacheConfig {
|
||||
bool enabled = false;
|
||||
int Fn_compute_blocks = 8;
|
||||
int Bn_compute_blocks = 0;
|
||||
float residual_diff_threshold = 0.08f;
|
||||
int max_warmup_steps = 8;
|
||||
int max_cached_steps = -1;
|
||||
int max_continuous_cached_steps = -1;
|
||||
float max_accumulated_residual_diff = -1.0f;
|
||||
std::vector<int> steps_computation_mask;
|
||||
bool scm_policy_dynamic = true;
|
||||
};
|
||||
|
||||
struct TaylorSeerConfig {
|
||||
bool enabled = false;
|
||||
int n_derivatives = 1;
|
||||
int max_warmup_steps = 2;
|
||||
int skip_interval_steps = 1;
|
||||
};
|
||||
|
||||
struct CacheDitConfig {
|
||||
DBCacheConfig dbcache;
|
||||
TaylorSeerConfig taylorseer;
|
||||
int double_Fn_blocks = -1;
|
||||
int double_Bn_blocks = -1;
|
||||
int single_Fn_blocks = -1;
|
||||
int single_Bn_blocks = -1;
|
||||
};
|
||||
|
||||
struct TaylorSeerState {
|
||||
int n_derivatives = 1;
|
||||
int current_step = -1;
|
||||
int last_computed_step = -1;
|
||||
std::vector<std::vector<float>> dY_prev;
|
||||
std::vector<std::vector<float>> dY_current;
|
||||
|
||||
void init(int n_deriv, size_t hidden_size) {
|
||||
n_derivatives = n_deriv;
|
||||
int order = n_derivatives + 1;
|
||||
dY_prev.resize(order);
|
||||
dY_current.resize(order);
|
||||
for (int i = 0; i < order; i++) {
|
||||
dY_prev[i].clear();
|
||||
dY_current[i].clear();
|
||||
}
|
||||
current_step = -1;
|
||||
last_computed_step = -1;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for (auto& v : dY_prev)
|
||||
v.clear();
|
||||
for (auto& v : dY_current)
|
||||
v.clear();
|
||||
current_step = -1;
|
||||
last_computed_step = -1;
|
||||
}
|
||||
|
||||
bool can_approximate() const {
|
||||
return last_computed_step >= n_derivatives && !dY_prev.empty() && !dY_prev[0].empty();
|
||||
}
|
||||
|
||||
void update_derivatives(const float* Y, size_t size, int step) {
|
||||
int order = n_derivatives + 1;
|
||||
dY_prev = dY_current;
|
||||
dY_current[0].resize(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
dY_current[0][i] = Y[i];
|
||||
}
|
||||
|
||||
int window = step - last_computed_step;
|
||||
if (window <= 0)
|
||||
window = 1;
|
||||
|
||||
for (int d = 0; d < n_derivatives; d++) {
|
||||
if (!dY_prev[d].empty() && dY_prev[d].size() == size) {
|
||||
dY_current[d + 1].resize(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
dY_current[d + 1][i] = (dY_current[d][i] - dY_prev[d][i]) / static_cast<float>(window);
|
||||
}
|
||||
} else {
|
||||
dY_current[d + 1].clear();
|
||||
}
|
||||
}
|
||||
|
||||
current_step = step;
|
||||
last_computed_step = step;
|
||||
}
|
||||
|
||||
void approximate(float* output, size_t size, int target_step) const {
|
||||
if (!can_approximate() || dY_prev[0].size() != size) {
|
||||
return;
|
||||
}
|
||||
|
||||
int elapsed = target_step - last_computed_step;
|
||||
if (elapsed <= 0)
|
||||
elapsed = 1;
|
||||
|
||||
std::fill(output, output + size, 0.0f);
|
||||
float factorial = 1.0f;
|
||||
int order = static_cast<int>(dY_prev.size());
|
||||
|
||||
for (int o = 0; o < order; o++) {
|
||||
if (dY_prev[o].empty() || dY_prev[o].size() != size)
|
||||
continue;
|
||||
if (o > 0)
|
||||
factorial *= static_cast<float>(o);
|
||||
float coeff = ::powf(static_cast<float>(elapsed), static_cast<float>(o)) / factorial;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
output[i] += coeff * dY_prev[o][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockCacheEntry {
|
||||
std::vector<float> residual_img;
|
||||
std::vector<float> residual_txt;
|
||||
std::vector<float> residual;
|
||||
std::vector<float> prev_img;
|
||||
std::vector<float> prev_txt;
|
||||
std::vector<float> prev_output;
|
||||
bool has_prev = false;
|
||||
};
|
||||
|
||||
struct CacheDitState {
|
||||
CacheDitConfig config;
|
||||
bool initialized = false;
|
||||
|
||||
int total_double_blocks = 0;
|
||||
int total_single_blocks = 0;
|
||||
size_t hidden_size = 0;
|
||||
|
||||
int current_step = -1;
|
||||
int total_steps = 0;
|
||||
int warmup_remaining = 0;
|
||||
std::vector<int> cached_steps;
|
||||
int continuous_cached_steps = 0;
|
||||
float accumulated_residual_diff = 0.0f;
|
||||
|
||||
std::vector<BlockCacheEntry> double_block_cache;
|
||||
std::vector<BlockCacheEntry> single_block_cache;
|
||||
|
||||
std::vector<float> Fn_residual_img;
|
||||
std::vector<float> Fn_residual_txt;
|
||||
std::vector<float> prev_Fn_residual_img;
|
||||
std::vector<float> prev_Fn_residual_txt;
|
||||
bool has_prev_Fn_residual = false;
|
||||
|
||||
std::vector<float> Bn_buffer_img;
|
||||
std::vector<float> Bn_buffer_txt;
|
||||
std::vector<float> Bn_buffer;
|
||||
bool has_Bn_buffer = false;
|
||||
|
||||
TaylorSeerState taylor_state;
|
||||
|
||||
bool can_cache_this_step = false;
|
||||
bool is_caching_this_step = false;
|
||||
|
||||
int total_blocks_computed = 0;
|
||||
int total_blocks_cached = 0;
|
||||
|
||||
void init(const CacheDitConfig& cfg, int num_double_blocks, int num_single_blocks, size_t h_size) {
|
||||
config = cfg;
|
||||
total_double_blocks = num_double_blocks;
|
||||
total_single_blocks = num_single_blocks;
|
||||
hidden_size = h_size;
|
||||
|
||||
initialized = cfg.dbcache.enabled || cfg.taylorseer.enabled;
|
||||
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
warmup_remaining = cfg.dbcache.max_warmup_steps;
|
||||
double_block_cache.resize(total_double_blocks);
|
||||
single_block_cache.resize(total_single_blocks);
|
||||
|
||||
if (cfg.taylorseer.enabled) {
|
||||
taylor_state.init(cfg.taylorseer.n_derivatives, h_size);
|
||||
}
|
||||
|
||||
reset_runtime();
|
||||
}
|
||||
|
||||
void reset_runtime() {
|
||||
current_step = -1;
|
||||
total_steps = 0;
|
||||
warmup_remaining = config.dbcache.max_warmup_steps;
|
||||
cached_steps.clear();
|
||||
continuous_cached_steps = 0;
|
||||
accumulated_residual_diff = 0.0f;
|
||||
|
||||
for (auto& entry : double_block_cache) {
|
||||
entry.residual_img.clear();
|
||||
entry.residual_txt.clear();
|
||||
entry.prev_img.clear();
|
||||
entry.prev_txt.clear();
|
||||
entry.has_prev = false;
|
||||
}
|
||||
|
||||
for (auto& entry : single_block_cache) {
|
||||
entry.residual.clear();
|
||||
entry.prev_output.clear();
|
||||
entry.has_prev = false;
|
||||
}
|
||||
|
||||
Fn_residual_img.clear();
|
||||
Fn_residual_txt.clear();
|
||||
prev_Fn_residual_img.clear();
|
||||
prev_Fn_residual_txt.clear();
|
||||
has_prev_Fn_residual = false;
|
||||
|
||||
Bn_buffer_img.clear();
|
||||
Bn_buffer_txt.clear();
|
||||
Bn_buffer.clear();
|
||||
has_Bn_buffer = false;
|
||||
|
||||
taylor_state.reset();
|
||||
|
||||
can_cache_this_step = false;
|
||||
is_caching_this_step = false;
|
||||
|
||||
total_blocks_computed = 0;
|
||||
total_blocks_cached = 0;
|
||||
}
|
||||
|
||||
bool enabled() const {
|
||||
return initialized && (config.dbcache.enabled || config.taylorseer.enabled);
|
||||
}
|
||||
|
||||
void begin_step(int step_index, float sigma = 0.0f) {
|
||||
if (!enabled())
|
||||
return;
|
||||
if (step_index == current_step)
|
||||
return;
|
||||
|
||||
current_step = step_index;
|
||||
total_steps++;
|
||||
|
||||
bool in_warmup = warmup_remaining > 0;
|
||||
if (in_warmup) {
|
||||
warmup_remaining--;
|
||||
}
|
||||
|
||||
bool scm_allows_cache = true;
|
||||
if (!config.dbcache.steps_computation_mask.empty()) {
|
||||
if (step_index < static_cast<int>(config.dbcache.steps_computation_mask.size())) {
|
||||
scm_allows_cache = (config.dbcache.steps_computation_mask[step_index] == 0);
|
||||
if (!config.dbcache.scm_policy_dynamic && scm_allows_cache) {
|
||||
can_cache_this_step = true;
|
||||
is_caching_this_step = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool max_cached_ok = (config.dbcache.max_cached_steps < 0) ||
|
||||
(static_cast<int>(cached_steps.size()) < config.dbcache.max_cached_steps);
|
||||
|
||||
bool max_cont_ok = (config.dbcache.max_continuous_cached_steps < 0) ||
|
||||
(continuous_cached_steps < config.dbcache.max_continuous_cached_steps);
|
||||
|
||||
bool accum_ok = (config.dbcache.max_accumulated_residual_diff < 0.0f) ||
|
||||
(accumulated_residual_diff < config.dbcache.max_accumulated_residual_diff);
|
||||
|
||||
can_cache_this_step = !in_warmup && scm_allows_cache && max_cached_ok && max_cont_ok && accum_ok && has_prev_Fn_residual;
|
||||
is_caching_this_step = false;
|
||||
}
|
||||
|
||||
void end_step(bool was_cached) {
|
||||
if (was_cached) {
|
||||
cached_steps.push_back(current_step);
|
||||
continuous_cached_steps++;
|
||||
} else {
|
||||
continuous_cached_steps = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static float calculate_residual_diff(const float* prev, const float* curr, size_t size) {
|
||||
if (size == 0)
|
||||
return 0.0f;
|
||||
|
||||
float sum_diff = 0.0f;
|
||||
float sum_abs = 0.0f;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
sum_diff += std::fabs(prev[i] - curr[i]);
|
||||
sum_abs += std::fabs(prev[i]);
|
||||
}
|
||||
|
||||
return sum_diff / (sum_abs + 1e-6f);
|
||||
}
|
||||
|
||||
static float calculate_residual_diff(const std::vector<float>& prev, const std::vector<float>& curr) {
|
||||
if (prev.size() != curr.size() || prev.empty())
|
||||
return 1.0f;
|
||||
return calculate_residual_diff(prev.data(), curr.data(), prev.size());
|
||||
}
|
||||
|
||||
int get_double_Fn_blocks() const {
|
||||
return (config.double_Fn_blocks >= 0) ? config.double_Fn_blocks : config.dbcache.Fn_compute_blocks;
|
||||
}
|
||||
|
||||
int get_double_Bn_blocks() const {
|
||||
return (config.double_Bn_blocks >= 0) ? config.double_Bn_blocks : config.dbcache.Bn_compute_blocks;
|
||||
}
|
||||
|
||||
int get_single_Fn_blocks() const {
|
||||
return (config.single_Fn_blocks >= 0) ? config.single_Fn_blocks : config.dbcache.Fn_compute_blocks;
|
||||
}
|
||||
|
||||
int get_single_Bn_blocks() const {
|
||||
return (config.single_Bn_blocks >= 0) ? config.single_Bn_blocks : config.dbcache.Bn_compute_blocks;
|
||||
}
|
||||
|
||||
bool is_Fn_double_block(int block_idx) const {
|
||||
return block_idx < get_double_Fn_blocks();
|
||||
}
|
||||
|
||||
bool is_Bn_double_block(int block_idx) const {
|
||||
int Bn = get_double_Bn_blocks();
|
||||
return Bn > 0 && block_idx >= (total_double_blocks - Bn);
|
||||
}
|
||||
|
||||
bool is_Mn_double_block(int block_idx) const {
|
||||
return !is_Fn_double_block(block_idx) && !is_Bn_double_block(block_idx);
|
||||
}
|
||||
|
||||
bool is_Fn_single_block(int block_idx) const {
|
||||
return block_idx < get_single_Fn_blocks();
|
||||
}
|
||||
|
||||
bool is_Bn_single_block(int block_idx) const {
|
||||
int Bn = get_single_Bn_blocks();
|
||||
return Bn > 0 && block_idx >= (total_single_blocks - Bn);
|
||||
}
|
||||
|
||||
bool is_Mn_single_block(int block_idx) const {
|
||||
return !is_Fn_single_block(block_idx) && !is_Bn_single_block(block_idx);
|
||||
}
|
||||
|
||||
void store_Fn_residual(const float* img, const float* txt, size_t img_size, size_t txt_size, const float* input_img, const float* input_txt) {
|
||||
Fn_residual_img.resize(img_size);
|
||||
Fn_residual_txt.resize(txt_size);
|
||||
|
||||
for (size_t i = 0; i < img_size; i++) {
|
||||
Fn_residual_img[i] = img[i] - input_img[i];
|
||||
}
|
||||
for (size_t i = 0; i < txt_size; i++) {
|
||||
Fn_residual_txt[i] = txt[i] - input_txt[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool check_cache_decision() {
|
||||
if (!can_cache_this_step) {
|
||||
is_caching_this_step = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!has_prev_Fn_residual || prev_Fn_residual_img.empty()) {
|
||||
is_caching_this_step = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
float diff_img = calculate_residual_diff(prev_Fn_residual_img, Fn_residual_img);
|
||||
float diff_txt = calculate_residual_diff(prev_Fn_residual_txt, Fn_residual_txt);
|
||||
float diff = (diff_img + diff_txt) / 2.0f;
|
||||
|
||||
if (diff < config.dbcache.residual_diff_threshold) {
|
||||
is_caching_this_step = true;
|
||||
accumulated_residual_diff += diff;
|
||||
return true;
|
||||
}
|
||||
|
||||
is_caching_this_step = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void update_prev_Fn_residual() {
|
||||
prev_Fn_residual_img = Fn_residual_img;
|
||||
prev_Fn_residual_txt = Fn_residual_txt;
|
||||
has_prev_Fn_residual = !prev_Fn_residual_img.empty();
|
||||
}
|
||||
|
||||
void store_double_block_residual(int block_idx, const float* img, const float* txt, size_t img_size, size_t txt_size, const float* prev_img, const float* prev_txt) {
|
||||
if (block_idx < 0 || block_idx >= static_cast<int>(double_block_cache.size()))
|
||||
return;
|
||||
|
||||
BlockCacheEntry& entry = double_block_cache[block_idx];
|
||||
|
||||
entry.residual_img.resize(img_size);
|
||||
entry.residual_txt.resize(txt_size);
|
||||
for (size_t i = 0; i < img_size; i++) {
|
||||
entry.residual_img[i] = img[i] - prev_img[i];
|
||||
}
|
||||
for (size_t i = 0; i < txt_size; i++) {
|
||||
entry.residual_txt[i] = txt[i] - prev_txt[i];
|
||||
}
|
||||
|
||||
entry.prev_img.resize(img_size);
|
||||
entry.prev_txt.resize(txt_size);
|
||||
for (size_t i = 0; i < img_size; i++) {
|
||||
entry.prev_img[i] = img[i];
|
||||
}
|
||||
for (size_t i = 0; i < txt_size; i++) {
|
||||
entry.prev_txt[i] = txt[i];
|
||||
}
|
||||
entry.has_prev = true;
|
||||
}
|
||||
|
||||
void apply_double_block_cache(int block_idx, float* img, float* txt, size_t img_size, size_t txt_size) {
|
||||
if (block_idx < 0 || block_idx >= static_cast<int>(double_block_cache.size()))
|
||||
return;
|
||||
|
||||
const BlockCacheEntry& entry = double_block_cache[block_idx];
|
||||
if (entry.residual_img.size() != img_size || entry.residual_txt.size() != txt_size)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < img_size; i++) {
|
||||
img[i] += entry.residual_img[i];
|
||||
}
|
||||
for (size_t i = 0; i < txt_size; i++) {
|
||||
txt[i] += entry.residual_txt[i];
|
||||
}
|
||||
|
||||
total_blocks_cached++;
|
||||
}
|
||||
|
||||
void store_single_block_residual(int block_idx, const float* output, size_t size, const float* input) {
|
||||
if (block_idx < 0 || block_idx >= static_cast<int>(single_block_cache.size()))
|
||||
return;
|
||||
|
||||
BlockCacheEntry& entry = single_block_cache[block_idx];
|
||||
|
||||
entry.residual.resize(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
entry.residual[i] = output[i] - input[i];
|
||||
}
|
||||
|
||||
entry.prev_output.resize(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
entry.prev_output[i] = output[i];
|
||||
}
|
||||
entry.has_prev = true;
|
||||
}
|
||||
|
||||
void apply_single_block_cache(int block_idx, float* output, size_t size) {
|
||||
if (block_idx < 0 || block_idx >= static_cast<int>(single_block_cache.size()))
|
||||
return;
|
||||
|
||||
const BlockCacheEntry& entry = single_block_cache[block_idx];
|
||||
if (entry.residual.size() != size)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
output[i] += entry.residual[i];
|
||||
}
|
||||
|
||||
total_blocks_cached++;
|
||||
}
|
||||
|
||||
void store_Bn_buffer(const float* img, const float* txt, size_t img_size, size_t txt_size, const float* Bn_start_img, const float* Bn_start_txt) {
|
||||
Bn_buffer_img.resize(img_size);
|
||||
Bn_buffer_txt.resize(txt_size);
|
||||
|
||||
for (size_t i = 0; i < img_size; i++) {
|
||||
Bn_buffer_img[i] = img[i] - Bn_start_img[i];
|
||||
}
|
||||
for (size_t i = 0; i < txt_size; i++) {
|
||||
Bn_buffer_txt[i] = txt[i] - Bn_start_txt[i];
|
||||
}
|
||||
has_Bn_buffer = true;
|
||||
}
|
||||
|
||||
void apply_Bn_buffer(float* img, float* txt, size_t img_size, size_t txt_size) {
|
||||
if (!has_Bn_buffer)
|
||||
return;
|
||||
if (Bn_buffer_img.size() != img_size || Bn_buffer_txt.size() != txt_size)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < img_size; i++) {
|
||||
img[i] += Bn_buffer_img[i];
|
||||
}
|
||||
for (size_t i = 0; i < txt_size; i++) {
|
||||
txt[i] += Bn_buffer_txt[i];
|
||||
}
|
||||
}
|
||||
|
||||
void taylor_update(const float* hidden_state, size_t size) {
|
||||
if (!config.taylorseer.enabled)
|
||||
return;
|
||||
taylor_state.update_derivatives(hidden_state, size, current_step);
|
||||
}
|
||||
|
||||
bool taylor_can_approximate() const {
|
||||
return config.taylorseer.enabled && taylor_state.can_approximate();
|
||||
}
|
||||
|
||||
void taylor_approximate(float* output, size_t size) {
|
||||
if (!config.taylorseer.enabled)
|
||||
return;
|
||||
taylor_state.approximate(output, size, current_step);
|
||||
}
|
||||
|
||||
bool should_use_taylor_this_step() const {
|
||||
if (!config.taylorseer.enabled)
|
||||
return false;
|
||||
if (current_step < config.taylorseer.max_warmup_steps)
|
||||
return false;
|
||||
|
||||
int interval = config.taylorseer.skip_interval_steps;
|
||||
if (interval <= 0)
|
||||
interval = 1;
|
||||
|
||||
return (current_step % (interval + 1)) != 0;
|
||||
}
|
||||
|
||||
void log_metrics() const {
|
||||
if (!enabled())
|
||||
return;
|
||||
|
||||
int total_blocks = total_blocks_computed + total_blocks_cached;
|
||||
float cache_ratio = (total_blocks > 0) ? (static_cast<float>(total_blocks_cached) / total_blocks * 100.0f) : 0.0f;
|
||||
|
||||
float step_cache_ratio = (total_steps > 0) ? (static_cast<float>(cached_steps.size()) / total_steps * 100.0f) : 0.0f;
|
||||
|
||||
LOG_INFO("CacheDIT: steps_cached=%zu/%d (%.1f%%), blocks_cached=%d/%d (%.1f%%), accum_diff=%.4f",
|
||||
cached_steps.size(), total_steps, step_cache_ratio,
|
||||
total_blocks_cached, total_blocks, cache_ratio,
|
||||
accumulated_residual_diff);
|
||||
}
|
||||
|
||||
std::string get_summary() const {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"CacheDIT[thresh=%.2f]: cached %zu/%d steps, %d/%d blocks",
|
||||
config.dbcache.residual_diff_threshold,
|
||||
cached_steps.size(), total_steps,
|
||||
total_blocks_cached, total_blocks_computed + total_blocks_cached);
|
||||
return std::string(buf);
|
||||
}
|
||||
};
|
||||
|
||||
inline std::vector<int> parse_scm_mask(const std::string& mask_str) {
|
||||
std::vector<int> mask;
|
||||
if (mask_str.empty())
|
||||
return mask;
|
||||
|
||||
size_t pos = 0;
|
||||
size_t start = 0;
|
||||
while ((pos = mask_str.find(',', start)) != std::string::npos) {
|
||||
std::string token = mask_str.substr(start, pos - start);
|
||||
mask.push_back(std::stoi(token));
|
||||
start = pos + 1;
|
||||
}
|
||||
if (start < mask_str.length()) {
|
||||
mask.push_back(std::stoi(mask_str.substr(start)));
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
inline std::vector<int> generate_scm_mask(
|
||||
const std::vector<int>& compute_bins,
|
||||
const std::vector<int>& cache_bins,
|
||||
int total_steps) {
|
||||
std::vector<int> mask;
|
||||
size_t c_idx = 0, cache_idx = 0;
|
||||
|
||||
while (static_cast<int>(mask.size()) < total_steps) {
|
||||
if (c_idx < compute_bins.size()) {
|
||||
for (int i = 0; i < compute_bins[c_idx] && static_cast<int>(mask.size()) < total_steps; i++) {
|
||||
mask.push_back(1);
|
||||
}
|
||||
c_idx++;
|
||||
}
|
||||
if (cache_idx < cache_bins.size()) {
|
||||
for (int i = 0; i < cache_bins[cache_idx] && static_cast<int>(mask.size()) < total_steps; i++) {
|
||||
mask.push_back(0);
|
||||
}
|
||||
cache_idx++;
|
||||
}
|
||||
if (c_idx >= compute_bins.size() && cache_idx >= cache_bins.size())
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mask.empty()) {
|
||||
mask.back() = 1;
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
inline std::vector<int> get_scm_preset(const std::string& preset, int total_steps) {
|
||||
struct Preset {
|
||||
std::vector<int> compute_bins;
|
||||
std::vector<int> cache_bins;
|
||||
};
|
||||
|
||||
Preset slow = {{8, 3, 3, 2, 1, 1}, {1, 2, 2, 2, 3}};
|
||||
Preset medium = {{6, 2, 2, 2, 2, 1}, {1, 3, 3, 3, 3}};
|
||||
Preset fast = {{6, 1, 1, 1, 1, 1}, {1, 3, 4, 5, 4}};
|
||||
Preset ultra = {{4, 1, 1, 1, 1}, {2, 5, 6, 7}};
|
||||
|
||||
Preset* p = nullptr;
|
||||
if (preset == "slow" || preset == "s" || preset == "S")
|
||||
p = &slow;
|
||||
else if (preset == "medium" || preset == "m" || preset == "M")
|
||||
p = &medium;
|
||||
else if (preset == "fast" || preset == "f" || preset == "F")
|
||||
p = &fast;
|
||||
else if (preset == "ultra" || preset == "u" || preset == "U")
|
||||
p = &ultra;
|
||||
else
|
||||
return {};
|
||||
|
||||
if (total_steps != 28 && total_steps > 0) {
|
||||
float scale = static_cast<float>(total_steps) / 28.0f;
|
||||
std::vector<int> scaled_compute, scaled_cache;
|
||||
|
||||
for (int v : p->compute_bins) {
|
||||
scaled_compute.push_back(std::max(1, static_cast<int>(v * scale + 0.5f)));
|
||||
}
|
||||
for (int v : p->cache_bins) {
|
||||
scaled_cache.push_back(std::max(1, static_cast<int>(v * scale + 0.5f)));
|
||||
}
|
||||
|
||||
return generate_scm_mask(scaled_compute, scaled_cache, total_steps);
|
||||
}
|
||||
|
||||
return generate_scm_mask(p->compute_bins, p->cache_bins, total_steps);
|
||||
}
|
||||
|
||||
inline float get_preset_threshold(const std::string& preset) {
|
||||
if (preset == "slow" || preset == "s" || preset == "S")
|
||||
return 0.20f;
|
||||
if (preset == "medium" || preset == "m" || preset == "M")
|
||||
return 0.25f;
|
||||
if (preset == "fast" || preset == "f" || preset == "F")
|
||||
return 0.30f;
|
||||
if (preset == "ultra" || preset == "u" || preset == "U")
|
||||
return 0.34f;
|
||||
return 0.08f;
|
||||
}
|
||||
|
||||
inline int get_preset_warmup(const std::string& preset) {
|
||||
if (preset == "slow" || preset == "s" || preset == "S")
|
||||
return 8;
|
||||
if (preset == "medium" || preset == "m" || preset == "M")
|
||||
return 6;
|
||||
if (preset == "fast" || preset == "f" || preset == "F")
|
||||
return 6;
|
||||
if (preset == "ultra" || preset == "u" || preset == "U")
|
||||
return 4;
|
||||
return 8;
|
||||
}
|
||||
|
||||
inline int get_preset_Fn(const std::string& preset) {
|
||||
if (preset == "slow" || preset == "s" || preset == "S")
|
||||
return 8;
|
||||
if (preset == "medium" || preset == "m" || preset == "M")
|
||||
return 8;
|
||||
if (preset == "fast" || preset == "f" || preset == "F")
|
||||
return 6;
|
||||
if (preset == "ultra" || preset == "u" || preset == "U")
|
||||
return 4;
|
||||
return 8;
|
||||
}
|
||||
|
||||
inline int get_preset_Bn(const std::string& preset) {
|
||||
(void)preset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void parse_dbcache_options(const std::string& opts, DBCacheConfig& cfg) {
|
||||
if (opts.empty())
|
||||
return;
|
||||
|
||||
int Fn = 8, Bn = 0, warmup = 8, max_cached = -1, max_cont = -1;
|
||||
float thresh = 0.08f;
|
||||
|
||||
sscanf(opts.c_str(), "%d,%d,%f,%d,%d,%d",
|
||||
&Fn, &Bn, &thresh, &warmup, &max_cached, &max_cont);
|
||||
|
||||
cfg.Fn_compute_blocks = Fn;
|
||||
cfg.Bn_compute_blocks = Bn;
|
||||
cfg.residual_diff_threshold = thresh;
|
||||
cfg.max_warmup_steps = warmup;
|
||||
cfg.max_cached_steps = max_cached;
|
||||
cfg.max_continuous_cached_steps = max_cont;
|
||||
}
|
||||
|
||||
inline void parse_taylorseer_options(const std::string& opts, TaylorSeerConfig& cfg) {
|
||||
if (opts.empty())
|
||||
return;
|
||||
|
||||
int n_deriv = 1, warmup = 2, interval = 1;
|
||||
sscanf(opts.c_str(), "%d,%d,%d", &n_deriv, &warmup, &interval);
|
||||
|
||||
cfg.n_derivatives = n_deriv;
|
||||
cfg.max_warmup_steps = warmup;
|
||||
cfg.skip_interval_steps = interval;
|
||||
}
|
||||
|
||||
struct CacheDitConditionState {
|
||||
DBCacheConfig config;
|
||||
TaylorSeerConfig taylor_config;
|
||||
bool initialized = false;
|
||||
|
||||
int current_step_index = -1;
|
||||
bool step_active = false;
|
||||
bool skip_current_step = false;
|
||||
bool initial_step = true;
|
||||
int warmup_remaining = 0;
|
||||
std::vector<int> cached_steps;
|
||||
int continuous_cached_steps = 0;
|
||||
float accumulated_residual_diff = 0.0f;
|
||||
int total_steps_skipped = 0;
|
||||
|
||||
const void* anchor_condition = nullptr;
|
||||
|
||||
struct CacheEntry {
|
||||
std::vector<float> diff;
|
||||
std::vector<float> prev_input;
|
||||
std::vector<float> prev_output;
|
||||
bool has_prev = false;
|
||||
};
|
||||
std::unordered_map<const void*, CacheEntry> cache_diffs;
|
||||
|
||||
TaylorSeerState taylor_state;
|
||||
|
||||
float start_sigma = std::numeric_limits<float>::max();
|
||||
float end_sigma = 0.0f;
|
||||
|
||||
void reset_runtime() {
|
||||
current_step_index = -1;
|
||||
step_active = false;
|
||||
skip_current_step = false;
|
||||
initial_step = true;
|
||||
warmup_remaining = config.max_warmup_steps;
|
||||
cached_steps.clear();
|
||||
continuous_cached_steps = 0;
|
||||
accumulated_residual_diff = 0.0f;
|
||||
total_steps_skipped = 0;
|
||||
anchor_condition = nullptr;
|
||||
cache_diffs.clear();
|
||||
taylor_state.reset();
|
||||
}
|
||||
|
||||
void init(const DBCacheConfig& dbcfg, const TaylorSeerConfig& tcfg) {
|
||||
config = dbcfg;
|
||||
taylor_config = tcfg;
|
||||
initialized = dbcfg.enabled || tcfg.enabled;
|
||||
reset_runtime();
|
||||
|
||||
if (taylor_config.enabled) {
|
||||
taylor_state.init(taylor_config.n_derivatives, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void set_sigmas(const std::vector<float>& sigmas) {
|
||||
if (!initialized || sigmas.size() < 2)
|
||||
return;
|
||||
|
||||
float start_percent = 0.15f;
|
||||
float end_percent = 0.95f;
|
||||
|
||||
size_t n_steps = sigmas.size() - 1;
|
||||
size_t start_step = static_cast<size_t>(start_percent * n_steps);
|
||||
size_t end_step = static_cast<size_t>(end_percent * n_steps);
|
||||
|
||||
if (start_step >= n_steps)
|
||||
start_step = n_steps - 1;
|
||||
if (end_step >= n_steps)
|
||||
end_step = n_steps - 1;
|
||||
|
||||
start_sigma = sigmas[start_step];
|
||||
end_sigma = sigmas[end_step];
|
||||
|
||||
if (start_sigma < end_sigma) {
|
||||
std::swap(start_sigma, end_sigma);
|
||||
}
|
||||
}
|
||||
|
||||
bool enabled() const {
|
||||
return initialized && (config.enabled || taylor_config.enabled);
|
||||
}
|
||||
|
||||
void begin_step(int step_index, float sigma) {
|
||||
if (!enabled())
|
||||
return;
|
||||
if (step_index == current_step_index)
|
||||
return;
|
||||
|
||||
current_step_index = step_index;
|
||||
skip_current_step = false;
|
||||
step_active = false;
|
||||
|
||||
if (sigma > start_sigma)
|
||||
return;
|
||||
if (!(sigma > end_sigma))
|
||||
return;
|
||||
|
||||
step_active = true;
|
||||
|
||||
if (warmup_remaining > 0) {
|
||||
warmup_remaining--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config.steps_computation_mask.empty()) {
|
||||
if (step_index < static_cast<int>(config.steps_computation_mask.size())) {
|
||||
if (config.steps_computation_mask[step_index] == 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.max_cached_steps >= 0 &&
|
||||
static_cast<int>(cached_steps.size()) >= config.max_cached_steps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.max_continuous_cached_steps >= 0 &&
|
||||
continuous_cached_steps >= config.max_continuous_cached_steps) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool step_is_active() const {
|
||||
return enabled() && step_active;
|
||||
}
|
||||
|
||||
bool is_step_skipped() const {
|
||||
return enabled() && step_active && skip_current_step;
|
||||
}
|
||||
|
||||
bool has_cache(const void* cond) const {
|
||||
auto it = cache_diffs.find(cond);
|
||||
return it != cache_diffs.end() && !it->second.diff.empty();
|
||||
}
|
||||
|
||||
void update_cache(const void* cond, const float* input, const float* output, size_t size) {
|
||||
CacheEntry& entry = cache_diffs[cond];
|
||||
entry.diff.resize(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
entry.diff[i] = output[i] - input[i];
|
||||
}
|
||||
|
||||
entry.prev_input.resize(size);
|
||||
entry.prev_output.resize(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
entry.prev_input[i] = input[i];
|
||||
entry.prev_output[i] = output[i];
|
||||
}
|
||||
entry.has_prev = true;
|
||||
}
|
||||
|
||||
void apply_cache(const void* cond, const float* input, float* output, size_t size) {
|
||||
auto it = cache_diffs.find(cond);
|
||||
if (it == cache_diffs.end() || it->second.diff.empty())
|
||||
return;
|
||||
if (it->second.diff.size() != size)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
output[i] = input[i] + it->second.diff[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool before_condition(const void* cond, struct ggml_tensor* input, struct ggml_tensor* output, float sigma, int step_index) {
|
||||
if (!enabled() || step_index < 0)
|
||||
return false;
|
||||
|
||||
if (step_index != current_step_index) {
|
||||
begin_step(step_index, sigma);
|
||||
}
|
||||
|
||||
if (!step_active)
|
||||
return false;
|
||||
|
||||
if (initial_step) {
|
||||
anchor_condition = cond;
|
||||
initial_step = false;
|
||||
}
|
||||
|
||||
bool is_anchor = (cond == anchor_condition);
|
||||
|
||||
if (skip_current_step) {
|
||||
if (has_cache(cond)) {
|
||||
apply_cache(cond, (float*)input->data, (float*)output->data,
|
||||
static_cast<size_t>(ggml_nelements(output)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_anchor)
|
||||
return false;
|
||||
|
||||
auto it = cache_diffs.find(cond);
|
||||
if (it == cache_diffs.end() || !it->second.has_prev)
|
||||
return false;
|
||||
|
||||
size_t ne = static_cast<size_t>(ggml_nelements(input));
|
||||
if (it->second.prev_input.size() != ne)
|
||||
return false;
|
||||
|
||||
float* input_data = (float*)input->data;
|
||||
float diff = CacheDitState::calculate_residual_diff(
|
||||
it->second.prev_input.data(), input_data, ne);
|
||||
|
||||
float effective_threshold = config.residual_diff_threshold;
|
||||
if (config.Fn_compute_blocks > 0) {
|
||||
float fn_confidence = 1.0f + 0.02f * (config.Fn_compute_blocks - 8);
|
||||
fn_confidence = std::max(0.5f, std::min(2.0f, fn_confidence));
|
||||
effective_threshold *= fn_confidence;
|
||||
}
|
||||
if (config.Bn_compute_blocks > 0) {
|
||||
float bn_quality = 1.0f - 0.03f * config.Bn_compute_blocks;
|
||||
bn_quality = std::max(0.5f, std::min(1.0f, bn_quality));
|
||||
effective_threshold *= bn_quality;
|
||||
}
|
||||
|
||||
if (diff < effective_threshold) {
|
||||
skip_current_step = true;
|
||||
total_steps_skipped++;
|
||||
cached_steps.push_back(current_step_index);
|
||||
continuous_cached_steps++;
|
||||
accumulated_residual_diff += diff;
|
||||
apply_cache(cond, input_data, (float*)output->data, ne);
|
||||
return true;
|
||||
}
|
||||
|
||||
continuous_cached_steps = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
void after_condition(const void* cond, struct ggml_tensor* input, struct ggml_tensor* output) {
|
||||
if (!step_is_active())
|
||||
return;
|
||||
|
||||
size_t ne = static_cast<size_t>(ggml_nelements(output));
|
||||
update_cache(cond, (float*)input->data, (float*)output->data, ne);
|
||||
|
||||
if (cond == anchor_condition && taylor_config.enabled) {
|
||||
taylor_state.update_derivatives((float*)output->data, ne, current_step_index);
|
||||
}
|
||||
}
|
||||
|
||||
void log_metrics() const {
|
||||
if (!enabled())
|
||||
return;
|
||||
|
||||
LOG_INFO("CacheDIT: steps_skipped=%d/%d (%.1f%%), accum_residual_diff=%.4f",
|
||||
total_steps_skipped,
|
||||
current_step_index + 1,
|
||||
(current_step_index > 0) ? (100.0f * total_steps_skipped / (current_step_index + 1)) : 0.0f,
|
||||
accumulated_residual_diff);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -3,34 +3,10 @@
|
||||
|
||||
#include "ggml_extend.hpp"
|
||||
#include "model.h"
|
||||
#include "tokenize_util.h"
|
||||
|
||||
/*================================================== CLIPTokenizer ===================================================*/
|
||||
|
||||
__STATIC_INLINE__ std::pair<std::unordered_map<std::string, float>, std::string> extract_and_remove_lora(std::string text) {
|
||||
std::regex re("<lora:([^:]+):([^>]+)>");
|
||||
std::smatch matches;
|
||||
std::unordered_map<std::string, float> filename2multiplier;
|
||||
|
||||
while (std::regex_search(text, matches, re)) {
|
||||
std::string filename = matches[1].str();
|
||||
float multiplier = std::stof(matches[2].str());
|
||||
|
||||
text = std::regex_replace(text, re, "", std::regex_constants::format_first_only);
|
||||
|
||||
if (multiplier == 0.f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filename2multiplier.find(filename) == filename2multiplier.end()) {
|
||||
filename2multiplier[filename] = multiplier;
|
||||
} else {
|
||||
filename2multiplier[filename] += multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(filename2multiplier, text);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::pair<int, std::u32string>> bytes_to_unicode() {
|
||||
std::vector<std::pair<int, std::u32string>> byte_unicode_pairs;
|
||||
std::set<int> byte_set;
|
||||
@@ -72,6 +48,8 @@ private:
|
||||
int encoder_len;
|
||||
int bpe_len;
|
||||
|
||||
std::vector<std::string> special_tokens;
|
||||
|
||||
public:
|
||||
const std::string UNK_TOKEN = "<|endoftext|>";
|
||||
const std::string BOS_TOKEN = "<|startoftext|>";
|
||||
@@ -117,6 +95,15 @@ private:
|
||||
return pairs;
|
||||
}
|
||||
|
||||
bool is_special_token(const std::string& token) {
|
||||
for (auto& special_token : special_tokens) {
|
||||
if (special_token == token) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
CLIPTokenizer(int pad_token_id = 49407, const std::string& merges_utf8_str = "")
|
||||
: PAD_TOKEN_ID(pad_token_id) {
|
||||
@@ -125,6 +112,8 @@ public:
|
||||
} else {
|
||||
load_from_merges(ModelLoader::load_merges());
|
||||
}
|
||||
add_special_token("<|startoftext|>");
|
||||
add_special_token("<|endoftext|>");
|
||||
}
|
||||
|
||||
void load_from_merges(const std::string& merges_utf8_str) {
|
||||
@@ -201,6 +190,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void add_special_token(const std::string& token) {
|
||||
special_tokens.push_back(token);
|
||||
}
|
||||
|
||||
std::u32string bpe(const std::u32string& token) {
|
||||
std::vector<std::u32string> word;
|
||||
|
||||
@@ -303,7 +296,7 @@ public:
|
||||
size_t max_length = 0,
|
||||
bool padding = false) {
|
||||
if (max_length > 0 && padding) {
|
||||
size_t n = std::ceil(tokens.size() * 1.0 / (max_length - 2));
|
||||
size_t n = static_cast<size_t>(std::ceil(tokens.size() * 1.0 / (max_length - 2)));
|
||||
if (n == 0) {
|
||||
n = 1;
|
||||
}
|
||||
@@ -379,25 +372,54 @@ public:
|
||||
return trim(text);
|
||||
}
|
||||
|
||||
std::vector<std::string> token_split(const std::string& text) {
|
||||
std::regex pat(R"('s|'t|'re|'ve|'m|'ll|'d|[[:alpha:]]+|[[:digit:]]|[^[:space:][:alpha:][:digit:]]+)",
|
||||
std::regex::icase);
|
||||
std::sregex_iterator iter(text.begin(), text.end(), pat);
|
||||
std::sregex_iterator end;
|
||||
|
||||
std::vector<std::string> result;
|
||||
for (; iter != end; ++iter) {
|
||||
result.emplace_back(iter->str());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<int> encode(std::string text, on_new_token_cb_t on_new_token_cb) {
|
||||
std::string original_text = text;
|
||||
std::vector<int32_t> bpe_tokens;
|
||||
text = whitespace_clean(text);
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
|
||||
std::regex pat(R"(<\|startoftext\|>|<\|endoftext\|>|'s|'t|'re|'ve|'m|'ll|'d|[[:alpha:]]+|[[:digit:]]|[^[:space:][:alpha:][:digit:]]+)",
|
||||
std::regex::icase);
|
||||
|
||||
std::smatch matches;
|
||||
std::string str = text;
|
||||
std::vector<std::string> token_strs;
|
||||
while (std::regex_search(str, matches, pat)) {
|
||||
bool skip = on_new_token_cb(str, bpe_tokens);
|
||||
if (skip) {
|
||||
|
||||
auto splited_texts = split_with_special_tokens(text, special_tokens);
|
||||
|
||||
for (auto& splited_text : splited_texts) {
|
||||
LOG_DEBUG("token %s", splited_text.c_str());
|
||||
if (is_special_token(splited_text)) {
|
||||
LOG_DEBUG("special %s", splited_text.c_str());
|
||||
bool skip = on_new_token_cb(splited_text, bpe_tokens);
|
||||
if (skip) {
|
||||
token_strs.push_back(splited_text);
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (auto& token : matches) {
|
||||
std::string token_str = token.str();
|
||||
|
||||
auto tokens = token_split(splited_text);
|
||||
for (auto& token : tokens) {
|
||||
if (on_new_token_cb != nullptr) {
|
||||
bool skip = on_new_token_cb(token, bpe_tokens);
|
||||
if (skip) {
|
||||
token_strs.push_back(token);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::string token_str = token;
|
||||
std::u32string utf32_token;
|
||||
for (int i = 0; i < token_str.length(); i++) {
|
||||
unsigned char b = token_str[i];
|
||||
@@ -417,14 +439,13 @@ public:
|
||||
bpe_tokens.push_back(encoder[bpe_str]);
|
||||
token_strs.push_back(utf32_to_utf8(bpe_str));
|
||||
}
|
||||
str = matches.suffix();
|
||||
}
|
||||
std::stringstream ss;
|
||||
ss << "[";
|
||||
for (auto token : token_strs) {
|
||||
ss << "\"" << token << "\", ";
|
||||
}
|
||||
ss << "]";
|
||||
// std::stringstream ss;
|
||||
// ss << "[";
|
||||
// for (auto token : token_strs) {
|
||||
// ss << "\"" << token << "\", ";
|
||||
// }
|
||||
// ss << "]";
|
||||
// LOG_DEBUG("split prompt \"%s\" to tokens %s", original_text.c_str(), ss.str().c_str());
|
||||
// printf("split prompt \"%s\" to tokens %s \n", original_text.c_str(), ss.str().c_str());
|
||||
return bpe_tokens;
|
||||
@@ -458,9 +479,9 @@ public:
|
||||
|
||||
x = fc1->forward(ctx, x);
|
||||
if (use_gelu) {
|
||||
x = ggml_gelu_inplace(ctx->ggml_ctx, x);
|
||||
x = ggml_ext_gelu(ctx->ggml_ctx, x, true);
|
||||
} else {
|
||||
x = ggml_gelu_quick_inplace(ctx->ggml_ctx, x);
|
||||
x = ggml_ext_gelu_quick(ctx->ggml_ctx, x, true);
|
||||
}
|
||||
x = fc2->forward(ctx, x);
|
||||
return x;
|
||||
@@ -489,7 +510,7 @@ public:
|
||||
blocks["mlp"] = std::shared_ptr<GGMLBlock>(new CLIPMLP(d_model, intermediate_size));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x, bool mask = true) {
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x, struct ggml_tensor* mask = nullptr) {
|
||||
// x: [N, n_token, d_model]
|
||||
auto self_attn = std::dynamic_pointer_cast<MultiheadAttention>(blocks["self_attn"]);
|
||||
auto layer_norm1 = std::dynamic_pointer_cast<LayerNorm>(blocks["layer_norm1"]);
|
||||
@@ -504,10 +525,10 @@ public:
|
||||
|
||||
struct CLIPEncoder : public GGMLBlock {
|
||||
protected:
|
||||
int64_t n_layer;
|
||||
int n_layer;
|
||||
|
||||
public:
|
||||
CLIPEncoder(int64_t n_layer,
|
||||
CLIPEncoder(int n_layer,
|
||||
int64_t d_model,
|
||||
int64_t n_head,
|
||||
int64_t intermediate_size,
|
||||
@@ -521,8 +542,8 @@ public:
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
struct ggml_tensor* x,
|
||||
int clip_skip = -1,
|
||||
bool mask = true) {
|
||||
struct ggml_tensor* mask = nullptr,
|
||||
int clip_skip = -1) {
|
||||
// x: [N, n_token, d_model]
|
||||
int layer_idx = n_layer - 1;
|
||||
// LOG_DEBUG("clip_skip %d", clip_skip);
|
||||
@@ -602,10 +623,10 @@ public:
|
||||
class CLIPVisionEmbeddings : public GGMLBlock {
|
||||
protected:
|
||||
int64_t embed_dim;
|
||||
int64_t num_channels;
|
||||
int64_t patch_size;
|
||||
int64_t image_size;
|
||||
int64_t num_patches;
|
||||
int num_channels;
|
||||
int patch_size;
|
||||
int image_size;
|
||||
int num_patches;
|
||||
int64_t num_positions;
|
||||
|
||||
void init_params(struct ggml_context* ctx, const String2TensorStorage& tensor_storage_map = {}, const std::string prefix = "") override {
|
||||
@@ -620,9 +641,9 @@ protected:
|
||||
|
||||
public:
|
||||
CLIPVisionEmbeddings(int64_t embed_dim,
|
||||
int64_t num_channels = 3,
|
||||
int64_t patch_size = 14,
|
||||
int64_t image_size = 224)
|
||||
int num_channels = 3,
|
||||
int patch_size = 14,
|
||||
int image_size = 224)
|
||||
: embed_dim(embed_dim),
|
||||
num_channels(num_channels),
|
||||
patch_size(patch_size),
|
||||
@@ -720,16 +741,17 @@ public:
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
struct ggml_tensor* input_ids,
|
||||
struct ggml_tensor* tkn_embeddings,
|
||||
size_t max_token_idx = 0,
|
||||
bool return_pooled = false,
|
||||
int clip_skip = -1) {
|
||||
struct ggml_tensor* mask = nullptr,
|
||||
size_t max_token_idx = 0,
|
||||
bool return_pooled = false,
|
||||
int clip_skip = -1) {
|
||||
// input_ids: [N, n_token]
|
||||
auto embeddings = std::dynamic_pointer_cast<CLIPEmbeddings>(blocks["embeddings"]);
|
||||
auto encoder = std::dynamic_pointer_cast<CLIPEncoder>(blocks["encoder"]);
|
||||
auto final_layer_norm = std::dynamic_pointer_cast<LayerNorm>(blocks["final_layer_norm"]);
|
||||
|
||||
auto x = embeddings->forward(ctx, input_ids, tkn_embeddings); // [N, n_token, hidden_size]
|
||||
x = encoder->forward(ctx, x, return_pooled ? -1 : clip_skip, true);
|
||||
x = encoder->forward(ctx, x, mask, return_pooled ? -1 : clip_skip);
|
||||
if (return_pooled || with_final_ln) {
|
||||
x = final_layer_norm->forward(ctx, x);
|
||||
}
|
||||
@@ -793,10 +815,11 @@ public:
|
||||
|
||||
auto x = embeddings->forward(ctx, pixel_values); // [N, num_positions, embed_dim]
|
||||
x = pre_layernorm->forward(ctx, x);
|
||||
x = encoder->forward(ctx, x, clip_skip, false);
|
||||
// print_ggml_tensor(x, true, "ClipVisionModel x: ");
|
||||
x = encoder->forward(ctx, x, nullptr, clip_skip);
|
||||
|
||||
auto last_hidden_state = x;
|
||||
x = post_layernorm->forward(ctx, x); // [N, n_token, hidden_size]
|
||||
|
||||
x = post_layernorm->forward(ctx, x); // [N, n_token, hidden_size]
|
||||
|
||||
GGML_ASSERT(x->ne[3] == 1);
|
||||
if (return_pooled) {
|
||||
@@ -884,6 +907,8 @@ public:
|
||||
struct CLIPTextModelRunner : public GGMLRunner {
|
||||
CLIPTextModel model;
|
||||
|
||||
std::vector<float> attention_mask_vec;
|
||||
|
||||
CLIPTextModelRunner(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
@@ -917,6 +942,7 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
struct ggml_tensor* input_ids,
|
||||
struct ggml_tensor* embeddings,
|
||||
struct ggml_tensor* mask,
|
||||
size_t max_token_idx = 0,
|
||||
bool return_pooled = false,
|
||||
int clip_skip = -1) {
|
||||
@@ -927,7 +953,7 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
input_ids = ggml_reshape_2d(ctx->ggml_ctx, input_ids, model.n_token, input_ids->ne[0] / model.n_token);
|
||||
}
|
||||
|
||||
return model.forward(ctx, input_ids, embeddings, max_token_idx, return_pooled, clip_skip);
|
||||
return model.forward(ctx, input_ids, embeddings, mask, max_token_idx, return_pooled, clip_skip);
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* input_ids,
|
||||
@@ -936,7 +962,7 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
size_t max_token_idx = 0,
|
||||
bool return_pooled = false,
|
||||
int clip_skip = -1) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
struct ggml_cgraph* gf = new_graph_custom(2048);
|
||||
|
||||
input_ids = to_backend(input_ids);
|
||||
|
||||
@@ -954,16 +980,30 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
embeddings = ggml_concat(compute_ctx, token_embed_weight, custom_embeddings, 1);
|
||||
}
|
||||
|
||||
int n_tokens = static_cast<int>(input_ids->ne[0]);
|
||||
attention_mask_vec.resize(n_tokens * n_tokens);
|
||||
for (int i0 = 0; i0 < n_tokens; i0++) {
|
||||
for (int i1 = 0; i1 < n_tokens; i1++) {
|
||||
float value = 0.f;
|
||||
if (i0 > i1) {
|
||||
value = -INFINITY;
|
||||
}
|
||||
attention_mask_vec[i1 * n_tokens + i0] = value;
|
||||
}
|
||||
}
|
||||
auto attention_mask = ggml_new_tensor_2d(compute_ctx, GGML_TYPE_F32, n_tokens, n_tokens);
|
||||
set_backend_tensor_data(attention_mask, attention_mask_vec.data());
|
||||
|
||||
auto runner_ctx = get_context();
|
||||
|
||||
struct ggml_tensor* hidden_states = forward(&runner_ctx, input_ids, embeddings, max_token_idx, return_pooled, clip_skip);
|
||||
struct ggml_tensor* hidden_states = forward(&runner_ctx, input_ids, embeddings, attention_mask, max_token_idx, return_pooled, clip_skip);
|
||||
|
||||
ggml_build_forward_expand(gf, hidden_states);
|
||||
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(const int n_threads,
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* input_ids,
|
||||
int num_custom_embeddings,
|
||||
void* custom_embeddings_data,
|
||||
@@ -975,7 +1015,7 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(input_ids, num_custom_embeddings, custom_embeddings_data, max_token_idx, return_pooled, clip_skip);
|
||||
};
|
||||
GGMLRunner::compute(get_graph, n_threads, true, output, output_ctx);
|
||||
return GGMLRunner::compute(get_graph, n_threads, true, output, output_ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
if (vae_downsample) {
|
||||
auto conv = std::dynamic_pointer_cast<Conv2d>(blocks["conv"]);
|
||||
|
||||
x = ggml_pad(ctx->ggml_ctx, x, 1, 1, 0, 0);
|
||||
x = ggml_ext_pad(ctx->ggml_ctx, x, 1, 1, 0, 0, ctx->circular_x_enabled, ctx->circular_y_enabled);
|
||||
x = conv->forward(ctx, x);
|
||||
} else {
|
||||
auto conv = std::dynamic_pointer_cast<Conv2d>(blocks["op"]);
|
||||
@@ -80,7 +80,7 @@ protected:
|
||||
std::pair<int, int> padding) {
|
||||
GGML_ASSERT(dims == 2 || dims == 3);
|
||||
if (dims == 3) {
|
||||
return std::shared_ptr<GGMLBlock>(new Conv3dnx1x1(in_channels, out_channels, kernel_size.first, 1, padding.first));
|
||||
return std::shared_ptr<GGMLBlock>(new Conv3d(in_channels, out_channels, {kernel_size.first, 1, 1}, {1, 1, 1}, {padding.first, 0, 0}));
|
||||
} else {
|
||||
return std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, out_channels, kernel_size, {1, 1}, padding));
|
||||
}
|
||||
@@ -182,33 +182,25 @@ protected:
|
||||
int64_t dim_in;
|
||||
int64_t dim_out;
|
||||
|
||||
void init_params(struct ggml_context* ctx, const String2TensorStorage& tensor_storage_map = {}, std::string prefix = "") override {
|
||||
enum ggml_type wtype = get_type(prefix + "proj.weight", tensor_storage_map, GGML_TYPE_F32);
|
||||
enum ggml_type bias_wtype = GGML_TYPE_F32;
|
||||
params["proj.weight"] = ggml_new_tensor_2d(ctx, wtype, dim_in, dim_out * 2);
|
||||
params["proj.bias"] = ggml_new_tensor_1d(ctx, bias_wtype, dim_out * 2);
|
||||
}
|
||||
|
||||
public:
|
||||
GEGLU(int64_t dim_in, int64_t dim_out)
|
||||
: dim_in(dim_in), dim_out(dim_out) {}
|
||||
: dim_in(dim_in), dim_out(dim_out) {
|
||||
blocks["proj"] = std::shared_ptr<GGMLBlock>(new Linear(dim_in, dim_out * 2));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) override {
|
||||
// x: [ne3, ne2, ne1, dim_in]
|
||||
// return: [ne3, ne2, ne1, dim_out]
|
||||
struct ggml_tensor* w = params["proj.weight"];
|
||||
struct ggml_tensor* b = params["proj.bias"];
|
||||
auto proj = std::dynamic_pointer_cast<Linear>(blocks["proj"]);
|
||||
|
||||
auto x_w = ggml_view_2d(ctx->ggml_ctx, w, w->ne[0], w->ne[1] / 2, w->nb[1], 0); // [dim_out, dim_in]
|
||||
auto x_b = ggml_view_1d(ctx->ggml_ctx, b, b->ne[0] / 2, 0); // [dim_out, dim_in]
|
||||
auto gate_w = ggml_view_2d(ctx->ggml_ctx, w, w->ne[0], w->ne[1] / 2, w->nb[1], w->nb[1] * w->ne[1] / 2); // [dim_out, ]
|
||||
auto gate_b = ggml_view_1d(ctx->ggml_ctx, b, b->ne[0] / 2, b->nb[0] * b->ne[0] / 2); // [dim_out, ]
|
||||
x = proj->forward(ctx, x); // [ne3, ne2, ne1, dim_out*2]
|
||||
auto x_vec = ggml_ext_chunk(ctx->ggml_ctx, x, 2, 0, false);
|
||||
x = x_vec[0]; // [ne3, ne2, ne1, dim_out]
|
||||
auto gate = x_vec[1]; // [ne3, ne2, ne1, dim_out]
|
||||
|
||||
auto x_in = x;
|
||||
x = ggml_ext_linear(ctx->ggml_ctx, x_in, x_w, x_b); // [ne3, ne2, ne1, dim_out]
|
||||
auto gate = ggml_ext_linear(ctx->ggml_ctx, x_in, gate_w, gate_b); // [ne3, ne2, ne1, dim_out]
|
||||
gate = ggml_cont(ctx->ggml_ctx, gate);
|
||||
|
||||
gate = ggml_gelu_inplace(ctx->ggml_ctx, gate);
|
||||
gate = ggml_ext_gelu(ctx->ggml_ctx, gate, true);
|
||||
|
||||
x = ggml_mul(ctx->ggml_ctx, x, gate); // [ne3, ne2, ne1, dim_out]
|
||||
|
||||
@@ -228,7 +220,7 @@ public:
|
||||
auto proj = std::dynamic_pointer_cast<Linear>(blocks["proj"]);
|
||||
|
||||
x = proj->forward(ctx, x);
|
||||
x = ggml_gelu_inplace(ctx->ggml_ctx, x);
|
||||
x = ggml_ext_gelu(ctx->ggml_ctx, x, true);
|
||||
return x;
|
||||
}
|
||||
};
|
||||
@@ -252,14 +244,18 @@ public:
|
||||
}
|
||||
|
||||
// net_1 is nn.Dropout(), skip for inference
|
||||
float scale = 1.f;
|
||||
bool force_prec_f32 = false;
|
||||
float scale = 1.f;
|
||||
if (precision_fix) {
|
||||
scale = 1.f / 128.f;
|
||||
#ifdef SD_USE_VULKAN
|
||||
force_prec_f32 = true;
|
||||
#endif
|
||||
}
|
||||
// The purpose of the scale here is to prevent NaN issues in certain situations.
|
||||
// For example, when using Vulkan without enabling force_prec_f32,
|
||||
// or when using CUDA but the weights are k-quants.
|
||||
blocks["net.2"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, dim_out, true, false, false, scale));
|
||||
blocks["net.2"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, dim_out, true, false, force_prec_f32, scale));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) {
|
||||
@@ -321,7 +317,7 @@ public:
|
||||
auto k = to_k->forward(ctx, context); // [N, n_context, inner_dim]
|
||||
auto v = to_v->forward(ctx, context); // [N, n_context, inner_dim]
|
||||
|
||||
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, n_head, nullptr, false, false, ctx->flash_attn_enabled); // [N, n_token, inner_dim]
|
||||
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, n_head, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, inner_dim]
|
||||
|
||||
x = to_out_0->forward(ctx, x); // [N, n_token, query_dim]
|
||||
return x;
|
||||
@@ -540,17 +536,17 @@ public:
|
||||
// image_only_indicator is always tensor([0.])
|
||||
float alpha = get_alpha();
|
||||
auto x = ggml_add(ctx->ggml_ctx,
|
||||
ggml_scale(ctx->ggml_ctx, x_spatial, alpha),
|
||||
ggml_scale(ctx->ggml_ctx, x_temporal, 1.0f - alpha));
|
||||
ggml_ext_scale(ctx->ggml_ctx, x_spatial, alpha),
|
||||
ggml_ext_scale(ctx->ggml_ctx, x_temporal, 1.0f - alpha));
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
class VideoResBlock : public ResBlock {
|
||||
public:
|
||||
VideoResBlock(int channels,
|
||||
int emb_channels,
|
||||
int out_channels,
|
||||
VideoResBlock(int64_t channels,
|
||||
int64_t emb_channels,
|
||||
int64_t out_channels,
|
||||
std::pair<int, int> kernel_size = {3, 3},
|
||||
int64_t video_kernel_size = 3,
|
||||
int dims = 2) // always 2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define __CONDITIONER_HPP__
|
||||
|
||||
#include "clip.hpp"
|
||||
#include "qwenvl.hpp"
|
||||
#include "llm.hpp"
|
||||
#include "t5.hpp"
|
||||
|
||||
struct SDCondition {
|
||||
@@ -34,6 +34,8 @@ struct Conditioner {
|
||||
virtual void free_params_buffer() = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) = 0;
|
||||
virtual size_t get_params_buffer_size() = 0;
|
||||
virtual void set_flash_attention_enabled(bool enabled) = 0;
|
||||
virtual void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) {}
|
||||
virtual std::tuple<SDCondition, std::vector<bool>> get_learned_condition_with_trigger(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const ConditionerParams& conditioner_params) {
|
||||
@@ -55,20 +57,26 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
std::shared_ptr<CLIPTextModelRunner> text_model2;
|
||||
|
||||
std::string trigger_word = "img"; // should be user settable
|
||||
std::string embd_dir;
|
||||
std::map<std::string, std::string> embedding_map;
|
||||
int32_t num_custom_embeddings = 0;
|
||||
int32_t num_custom_embeddings_2 = 0;
|
||||
std::vector<uint8_t> token_embed_custom;
|
||||
std::vector<std::string> readed_embeddings;
|
||||
std::map<std::string, std::pair<int, int>> embedding_pos_map;
|
||||
|
||||
FrozenCLIPEmbedderWithCustomWords(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string& embd_dir,
|
||||
const std::map<std::string, std::string>& orig_embedding_map,
|
||||
SDVersion version = VERSION_SD1,
|
||||
PMVersion pv = PM_VERSION_1)
|
||||
: version(version), pm_version(pv), tokenizer(sd_version_is_sd2(version) ? 0 : 49407), embd_dir(embd_dir) {
|
||||
bool force_clip_f32 = embd_dir.size() > 0;
|
||||
: version(version), pm_version(pv), tokenizer(sd_version_is_sd2(version) ? 0 : 49407) {
|
||||
for (const auto& kv : orig_embedding_map) {
|
||||
std::string name = kv.first;
|
||||
std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
embedding_map[name] = kv.second;
|
||||
tokenizer.add_special_token(name);
|
||||
}
|
||||
bool force_clip_f32 = !embedding_map.empty();
|
||||
if (sd_version_is_sd1(version)) {
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, offload_params_to_cpu, tensor_storage_map, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, true, force_clip_f32);
|
||||
} else if (sd_version_is_sd2(version)) {
|
||||
@@ -108,15 +116,32 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void set_flash_attention_enabled(bool enabled) override {
|
||||
text_model->set_flash_attention_enabled(enabled);
|
||||
if (sd_version_is_sdxl(version)) {
|
||||
text_model2->set_flash_attention_enabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
text_model->set_weight_adapter(adapter);
|
||||
if (sd_version_is_sdxl(version)) {
|
||||
text_model2->set_weight_adapter(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
bool load_embedding(std::string embd_name, std::string embd_path, std::vector<int32_t>& bpe_tokens) {
|
||||
// the order matters
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file_and_convert_name(embd_path)) {
|
||||
LOG_ERROR("embedding '%s' failed", embd_name.c_str());
|
||||
return false;
|
||||
}
|
||||
if (std::find(readed_embeddings.begin(), readed_embeddings.end(), embd_name) != readed_embeddings.end()) {
|
||||
auto iter = embedding_pos_map.find(embd_name);
|
||||
if (iter != embedding_pos_map.end()) {
|
||||
LOG_DEBUG("embedding already read in: %s", embd_name.c_str());
|
||||
for (int i = iter->second.first; i < iter->second.second; i++) {
|
||||
bpe_tokens.push_back(text_model->model.vocab_size + i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
struct ggml_init_params params;
|
||||
@@ -147,7 +172,7 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
return true;
|
||||
};
|
||||
model_loader.load_tensors(on_load, 1);
|
||||
readed_embeddings.push_back(embd_name);
|
||||
int pos_start = num_custom_embeddings;
|
||||
if (embd) {
|
||||
int64_t hidden_size = text_model->model.hidden_size;
|
||||
token_embed_custom.resize(token_embed_custom.size() + ggml_nbytes(embd));
|
||||
@@ -174,6 +199,11 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
}
|
||||
LOG_DEBUG("embedding '%s' applied, custom embeddings: %i (text model 2)", embd_name.c_str(), num_custom_embeddings_2);
|
||||
}
|
||||
int pos_end = num_custom_embeddings;
|
||||
if (pos_end == pos_start) {
|
||||
return false;
|
||||
}
|
||||
embedding_pos_map[embd_name] = std::pair{pos_start, pos_end};
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -188,25 +218,13 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
|
||||
std::vector<int> convert_token_to_id(std::string text) {
|
||||
auto on_new_token_cb = [&](std::string& str, std::vector<int32_t>& bpe_tokens) -> bool {
|
||||
size_t word_end = str.find(",");
|
||||
std::string embd_name = word_end == std::string::npos ? str : str.substr(0, word_end);
|
||||
embd_name = trim(embd_name);
|
||||
std::string embd_path = get_full_path(embd_dir, embd_name + ".pt");
|
||||
if (embd_path.size() == 0) {
|
||||
embd_path = get_full_path(embd_dir, embd_name + ".ckpt");
|
||||
auto iter = embedding_map.find(str);
|
||||
if (iter == embedding_map.end()) {
|
||||
return false;
|
||||
}
|
||||
if (embd_path.size() == 0) {
|
||||
embd_path = get_full_path(embd_dir, embd_name + ".safetensors");
|
||||
}
|
||||
if (embd_path.size() > 0) {
|
||||
if (load_embedding(embd_name, embd_path, bpe_tokens)) {
|
||||
if (word_end != std::string::npos) {
|
||||
str = str.substr(word_end);
|
||||
} else {
|
||||
str = "";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::string embedding_path = iter->second;
|
||||
if (load_embedding(str, embedding_path, bpe_tokens)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -237,25 +255,13 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
}
|
||||
|
||||
auto on_new_token_cb = [&](std::string& str, std::vector<int32_t>& bpe_tokens) -> bool {
|
||||
size_t word_end = str.find(",");
|
||||
std::string embd_name = word_end == std::string::npos ? str : str.substr(0, word_end);
|
||||
embd_name = trim(embd_name);
|
||||
std::string embd_path = get_full_path(embd_dir, embd_name + ".pt");
|
||||
if (embd_path.size() == 0) {
|
||||
embd_path = get_full_path(embd_dir, embd_name + ".ckpt");
|
||||
auto iter = embedding_map.find(str);
|
||||
if (iter == embedding_map.end()) {
|
||||
return false;
|
||||
}
|
||||
if (embd_path.size() == 0) {
|
||||
embd_path = get_full_path(embd_dir, embd_name + ".safetensors");
|
||||
}
|
||||
if (embd_path.size() > 0) {
|
||||
if (load_embedding(embd_name, embd_path, bpe_tokens)) {
|
||||
if (word_end != std::string::npos) {
|
||||
str = str.substr(word_end);
|
||||
} else {
|
||||
str = "";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::string embedding_path = iter->second;
|
||||
if (load_embedding(str, embedding_path, bpe_tokens)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -270,13 +276,30 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
const std::string& curr_text = item.first;
|
||||
float curr_weight = item.second;
|
||||
// printf(" %s: %f \n", curr_text.c_str(), curr_weight);
|
||||
int32_t clean_index = 0;
|
||||
if (curr_text == "BREAK" && curr_weight == -1.0f) {
|
||||
// Pad token array up to chunk size at this point.
|
||||
// TODO: This is a hardcoded chunk_len, like in stable-diffusion.cpp, make it a parameter for the future?
|
||||
// Also, this is 75 instead of 77 to leave room for BOS and EOS tokens.
|
||||
int padding_size = 75 - (tokens_acc % 75);
|
||||
for (int j = 0; j < padding_size; j++) {
|
||||
clean_input_ids.push_back(tokenizer.EOS_TOKEN_ID);
|
||||
clean_index++;
|
||||
}
|
||||
|
||||
// After padding, continue to the next iteration to process the following text as a new segment
|
||||
tokens.insert(tokens.end(), clean_input_ids.begin(), clean_input_ids.end());
|
||||
weights.insert(weights.end(), padding_size, curr_weight);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Regular token, process normally
|
||||
std::vector<int> curr_tokens = tokenizer.encode(curr_text, on_new_token_cb);
|
||||
int32_t clean_index = 0;
|
||||
for (uint32_t i = 0; i < curr_tokens.size(); i++) {
|
||||
int token_id = curr_tokens[i];
|
||||
if (token_id == image_token)
|
||||
if (token_id == image_token) {
|
||||
class_token_index.push_back(clean_index - 1);
|
||||
else {
|
||||
} else {
|
||||
clean_input_ids.push_back(token_id);
|
||||
clean_index++;
|
||||
}
|
||||
@@ -288,11 +311,11 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
int class_token = clean_input_ids[class_token_index[0]];
|
||||
class_idx = tokens_acc + class_token_index[0];
|
||||
std::vector<int> clean_input_ids_tmp;
|
||||
for (uint32_t i = 0; i < class_token_index[0]; i++)
|
||||
for (int i = 0; i < class_token_index[0]; i++)
|
||||
clean_input_ids_tmp.push_back(clean_input_ids[i]);
|
||||
for (uint32_t i = 0; i < (pm_version == PM_VERSION_2 ? 2 * num_input_imgs : num_input_imgs); i++)
|
||||
for (int i = 0; i < (pm_version == PM_VERSION_2 ? 2 * num_input_imgs : num_input_imgs); i++)
|
||||
clean_input_ids_tmp.push_back(class_token);
|
||||
for (uint32_t i = class_token_index[0] + 1; i < clean_input_ids.size(); i++)
|
||||
for (int i = class_token_index[0] + 1; i < clean_input_ids.size(); i++)
|
||||
clean_input_ids_tmp.push_back(clean_input_ids[i]);
|
||||
clean_input_ids.clear();
|
||||
clean_input_ids = clean_input_ids_tmp;
|
||||
@@ -307,7 +330,7 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
|
||||
tokenizer.pad_tokens(tokens, weights, max_length, padding);
|
||||
int offset = pm_version == PM_VERSION_2 ? 2 * num_input_imgs : num_input_imgs;
|
||||
for (uint32_t i = 0; i < tokens.size(); i++) {
|
||||
for (int i = 0; i < tokens.size(); i++) {
|
||||
// if (class_idx + 1 <= i && i < class_idx + 1 + 2*num_input_imgs) // photomaker V2 has num_tokens(=2)*num_input_imgs
|
||||
if (class_idx + 1 <= i && i < class_idx + 1 + offset) // photomaker V2 has num_tokens(=2)*num_input_imgs
|
||||
// hardcode for now
|
||||
@@ -351,25 +374,13 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
}
|
||||
|
||||
auto on_new_token_cb = [&](std::string& str, std::vector<int32_t>& bpe_tokens) -> bool {
|
||||
size_t word_end = str.find(",");
|
||||
std::string embd_name = word_end == std::string::npos ? str : str.substr(0, word_end);
|
||||
embd_name = trim(embd_name);
|
||||
std::string embd_path = get_full_path(embd_dir, embd_name + ".pt");
|
||||
if (embd_path.size() == 0) {
|
||||
embd_path = get_full_path(embd_dir, embd_name + ".ckpt");
|
||||
auto iter = embedding_map.find(str);
|
||||
if (iter == embedding_map.end()) {
|
||||
return false;
|
||||
}
|
||||
if (embd_path.size() == 0) {
|
||||
embd_path = get_full_path(embd_dir, embd_name + ".safetensors");
|
||||
}
|
||||
if (embd_path.size() > 0) {
|
||||
if (load_embedding(embd_name, embd_path, bpe_tokens)) {
|
||||
if (word_end != std::string::npos) {
|
||||
str = str.substr(word_end);
|
||||
} else {
|
||||
str = "";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::string embedding_path = iter->second;
|
||||
if (load_embedding(str, embedding_path, bpe_tokens)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -379,6 +390,22 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
for (const auto& item : parsed_attention) {
|
||||
const std::string& curr_text = item.first;
|
||||
float curr_weight = item.second;
|
||||
|
||||
if (curr_text == "BREAK" && curr_weight == -1.0f) {
|
||||
// Pad token array up to chunk size at this point.
|
||||
// TODO: This is a hardcoded chunk_len, like in stable-diffusion.cpp, make it a parameter for the future?
|
||||
// Also, this is 75 instead of 77 to leave room for BOS and EOS tokens.
|
||||
size_t current_size = tokens.size();
|
||||
size_t padding_size = (75 - (current_size % 75)) % 75; // Ensure no negative padding
|
||||
|
||||
if (padding_size > 0) {
|
||||
LOG_DEBUG("BREAK token encountered, padding current chunk by %zu tokens.", padding_size);
|
||||
tokens.insert(tokens.end(), padding_size, tokenizer.EOS_TOKEN_ID);
|
||||
weights.insert(weights.end(), padding_size, 1.0f);
|
||||
}
|
||||
continue; // Skip to the next item after handling BREAK
|
||||
}
|
||||
|
||||
std::vector<int> curr_tokens = tokenizer.encode(curr_text, on_new_token_cb);
|
||||
tokens.insert(tokens.end(), curr_tokens.begin(), curr_tokens.end());
|
||||
weights.insert(weights.end(), curr_tokens.size(), curr_weight);
|
||||
@@ -662,7 +689,7 @@ struct FrozenCLIPVisionEmbedder : public GGMLRunner {
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(const int n_threads,
|
||||
bool compute(const int n_threads,
|
||||
ggml_tensor* pixel_values,
|
||||
bool return_pooled,
|
||||
int clip_skip,
|
||||
@@ -671,7 +698,7 @@ struct FrozenCLIPVisionEmbedder : public GGMLRunner {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(pixel_values, return_pooled, clip_skip);
|
||||
};
|
||||
GGMLRunner::compute(get_graph, n_threads, true, output, output_ctx);
|
||||
return GGMLRunner::compute(get_graph, n_threads, true, output, output_ctx);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -764,6 +791,30 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void set_flash_attention_enabled(bool enabled) override {
|
||||
if (clip_l) {
|
||||
clip_l->set_flash_attention_enabled(enabled);
|
||||
}
|
||||
if (clip_g) {
|
||||
clip_g->set_flash_attention_enabled(enabled);
|
||||
}
|
||||
if (t5) {
|
||||
t5->set_flash_attention_enabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
if (clip_l) {
|
||||
clip_l->set_weight_adapter(adapter);
|
||||
}
|
||||
if (clip_g) {
|
||||
clip_g->set_weight_adapter(adapter);
|
||||
}
|
||||
if (t5) {
|
||||
t5->set_weight_adapter(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::vector<int>, std::vector<float>>> tokenize(std::string text,
|
||||
size_t max_length = 0,
|
||||
bool padding = false) {
|
||||
@@ -1160,6 +1211,24 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void set_flash_attention_enabled(bool enabled) override {
|
||||
if (clip_l) {
|
||||
clip_l->set_flash_attention_enabled(enabled);
|
||||
}
|
||||
if (t5) {
|
||||
t5->set_flash_attention_enabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) {
|
||||
if (clip_l) {
|
||||
clip_l->set_weight_adapter(adapter);
|
||||
}
|
||||
if (t5) {
|
||||
t5->set_weight_adapter(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::vector<int>, std::vector<float>>> tokenize(std::string text,
|
||||
size_t max_length = 0,
|
||||
bool padding = false) {
|
||||
@@ -1400,6 +1469,18 @@ struct T5CLIPEmbedder : public Conditioner {
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void set_flash_attention_enabled(bool enabled) override {
|
||||
if (t5) {
|
||||
t5->set_flash_attention_enabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
if (t5) {
|
||||
t5->set_weight_adapter(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<std::vector<int>, std::vector<float>, std::vector<float>> tokenize(std::string text,
|
||||
size_t max_length = 0,
|
||||
bool padding = false) {
|
||||
@@ -1538,7 +1619,7 @@ struct T5CLIPEmbedder : public Conditioner {
|
||||
chunk_hidden_states->ne[0],
|
||||
ggml_nelements(hidden_states) / chunk_hidden_states->ne[0]);
|
||||
|
||||
modify_mask_to_attend_padding(t5_attn_mask, ggml_nelements(t5_attn_mask), mask_pad);
|
||||
modify_mask_to_attend_padding(t5_attn_mask, static_cast<int>(ggml_nelements(t5_attn_mask)), mask_pad);
|
||||
|
||||
return {hidden_states, t5_attn_mask, nullptr};
|
||||
}
|
||||
@@ -1555,55 +1636,78 @@ struct T5CLIPEmbedder : public Conditioner {
|
||||
}
|
||||
};
|
||||
|
||||
struct Qwen2_5_VLCLIPEmbedder : public Conditioner {
|
||||
Qwen::Qwen2Tokenizer tokenizer;
|
||||
std::shared_ptr<Qwen::Qwen2_5_VLRunner> qwenvl;
|
||||
struct LLMEmbedder : public Conditioner {
|
||||
SDVersion version;
|
||||
std::shared_ptr<LLM::BPETokenizer> tokenizer;
|
||||
std::shared_ptr<LLM::LLMRunner> llm;
|
||||
|
||||
Qwen2_5_VLCLIPEmbedder(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
bool enable_vision = false) {
|
||||
qwenvl = std::make_shared<Qwen::Qwen2_5_VLRunner>(backend,
|
||||
offload_params_to_cpu,
|
||||
tensor_storage_map,
|
||||
"text_encoders.qwen2vl",
|
||||
enable_vision);
|
||||
LLMEmbedder(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
SDVersion version = VERSION_QWEN_IMAGE,
|
||||
const std::string prefix = "",
|
||||
bool enable_vision = false)
|
||||
: version(version) {
|
||||
LLM::LLMArch arch = LLM::LLMArch::QWEN2_5_VL;
|
||||
if (version == VERSION_FLUX2) {
|
||||
arch = LLM::LLMArch::MISTRAL_SMALL_3_2;
|
||||
} else if (sd_version_is_z_image(version) || version == VERSION_OVIS_IMAGE || version == VERSION_FLUX2_KLEIN) {
|
||||
arch = LLM::LLMArch::QWEN3;
|
||||
}
|
||||
if (arch == LLM::LLMArch::MISTRAL_SMALL_3_2) {
|
||||
tokenizer = std::make_shared<LLM::MistralTokenizer>();
|
||||
} else {
|
||||
tokenizer = std::make_shared<LLM::Qwen2Tokenizer>();
|
||||
}
|
||||
llm = std::make_shared<LLM::LLMRunner>(arch,
|
||||
backend,
|
||||
offload_params_to_cpu,
|
||||
tensor_storage_map,
|
||||
"text_encoders.llm",
|
||||
enable_vision);
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) override {
|
||||
qwenvl->get_param_tensors(tensors, "text_encoders.qwen2vl");
|
||||
llm->get_param_tensors(tensors, "text_encoders.llm");
|
||||
}
|
||||
|
||||
void alloc_params_buffer() override {
|
||||
qwenvl->alloc_params_buffer();
|
||||
llm->alloc_params_buffer();
|
||||
}
|
||||
|
||||
void free_params_buffer() override {
|
||||
qwenvl->free_params_buffer();
|
||||
llm->free_params_buffer();
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() override {
|
||||
size_t buffer_size = 0;
|
||||
buffer_size += qwenvl->get_params_buffer_size();
|
||||
buffer_size += llm->get_params_buffer_size();
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void set_flash_attention_enabled(bool enabled) override {
|
||||
llm->set_flash_attention_enabled(enabled);
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
if (llm) {
|
||||
llm->set_weight_adapter(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<std::vector<int>, std::vector<float>> tokenize(std::string text,
|
||||
size_t max_length = 0,
|
||||
size_t system_prompt_length = 0,
|
||||
bool padding = false) {
|
||||
std::pair<int, int> attn_range,
|
||||
size_t max_length = 0,
|
||||
bool padding = false) {
|
||||
std::vector<std::pair<std::string, float>> parsed_attention;
|
||||
if (system_prompt_length > 0) {
|
||||
parsed_attention.emplace_back(text.substr(0, system_prompt_length), 1.f);
|
||||
auto new_parsed_attention = parse_prompt_attention(text.substr(system_prompt_length, text.size() - system_prompt_length));
|
||||
parsed_attention.emplace_back(text.substr(0, attn_range.first), 1.f);
|
||||
if (attn_range.second - attn_range.first > 0) {
|
||||
auto new_parsed_attention = parse_prompt_attention(text.substr(attn_range.first, attn_range.second - attn_range.first));
|
||||
parsed_attention.insert(parsed_attention.end(),
|
||||
new_parsed_attention.begin(),
|
||||
new_parsed_attention.end());
|
||||
} else {
|
||||
parsed_attention = parse_prompt_attention(text);
|
||||
}
|
||||
|
||||
parsed_attention.emplace_back(text.substr(attn_range.second), 1.f);
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[";
|
||||
@@ -1619,12 +1723,12 @@ struct Qwen2_5_VLCLIPEmbedder : public Conditioner {
|
||||
for (const auto& item : parsed_attention) {
|
||||
const std::string& curr_text = item.first;
|
||||
float curr_weight = item.second;
|
||||
std::vector<int> curr_tokens = tokenizer.tokenize(curr_text, nullptr);
|
||||
std::vector<int> curr_tokens = tokenizer->tokenize(curr_text, nullptr);
|
||||
tokens.insert(tokens.end(), curr_tokens.begin(), curr_tokens.end());
|
||||
weights.insert(weights.end(), curr_tokens.size(), curr_weight);
|
||||
}
|
||||
|
||||
tokenizer.pad_tokens(tokens, weights, max_length, padding);
|
||||
tokenizer->pad_tokens(tokens, weights, max_length, padding);
|
||||
|
||||
// for (int i = 0; i < tokens.size(); i++) {
|
||||
// std::cout << tokens[i] << ":" << weights[i] << ", " << i << std::endl;
|
||||
@@ -1639,9 +1743,14 @@ struct Qwen2_5_VLCLIPEmbedder : public Conditioner {
|
||||
const ConditionerParams& conditioner_params) override {
|
||||
std::string prompt;
|
||||
std::vector<std::pair<int, ggml_tensor*>> image_embeds;
|
||||
size_t system_prompt_length = 0;
|
||||
std::pair<int, int> prompt_attn_range;
|
||||
int prompt_template_encode_start_idx = 34;
|
||||
if (qwenvl->enable_vision && conditioner_params.ref_images.size() > 0) {
|
||||
int max_length = 0;
|
||||
std::set<int> out_layers;
|
||||
std::vector<int> tokens;
|
||||
std::vector<float> weights;
|
||||
std::vector<float> mask;
|
||||
if (llm->enable_vision && conditioner_params.ref_images.size() > 0) {
|
||||
LOG_INFO("QwenImageEditPlusPipeline");
|
||||
prompt_template_encode_start_idx = 64;
|
||||
int image_embed_idx = 64 + 6;
|
||||
@@ -1653,11 +1762,11 @@ struct Qwen2_5_VLCLIPEmbedder : public Conditioner {
|
||||
|
||||
for (int i = 0; i < conditioner_params.ref_images.size(); i++) {
|
||||
sd_image_f32_t image = sd_image_t_to_sd_image_f32_t(*conditioner_params.ref_images[i]);
|
||||
double factor = qwenvl->params.vision.patch_size * qwenvl->params.vision.spatial_merge_size;
|
||||
double factor = llm->params.vision.patch_size * llm->params.vision.spatial_merge_size;
|
||||
int height = image.height;
|
||||
int width = image.width;
|
||||
int h_bar = static_cast<int>(std::round(height / factor)) * factor;
|
||||
int w_bar = static_cast<int>(std::round(width / factor)) * factor;
|
||||
int h_bar = static_cast<int>(std::round(height / factor) * factor);
|
||||
int w_bar = static_cast<int>(std::round(width / factor) * factor);
|
||||
|
||||
if (static_cast<double>(h_bar) * w_bar > max_pixels) {
|
||||
double beta = std::sqrt((height * width) / static_cast<double>(max_pixels));
|
||||
@@ -1683,9 +1792,9 @@ struct Qwen2_5_VLCLIPEmbedder : public Conditioner {
|
||||
resized_image.data = nullptr;
|
||||
|
||||
ggml_tensor* image_embed = nullptr;
|
||||
qwenvl->encode_image(n_threads, image_tensor, &image_embed, work_ctx);
|
||||
llm->encode_image(n_threads, image_tensor, &image_embed, work_ctx);
|
||||
image_embeds.emplace_back(image_embed_idx, image_embed);
|
||||
image_embed_idx += 1 + image_embed->ne[1] + 6;
|
||||
image_embed_idx += 1 + static_cast<int>(image_embed->ne[1]) + 6;
|
||||
|
||||
img_prompt += "Picture " + std::to_string(i + 1) + ": <|vision_start|>"; // [24669, 220, index, 25, 220, 151652]
|
||||
int64_t num_image_tokens = image_embed->ne[1];
|
||||
@@ -1697,30 +1806,112 @@ struct Qwen2_5_VLCLIPEmbedder : public Conditioner {
|
||||
}
|
||||
|
||||
prompt = "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.<|im_end|>\n<|im_start|>user\n";
|
||||
|
||||
system_prompt_length = prompt.size();
|
||||
|
||||
prompt += img_prompt;
|
||||
|
||||
prompt_attn_range.first = static_cast<int>(prompt.size());
|
||||
prompt += conditioner_params.text;
|
||||
prompt_attn_range.second = static_cast<int>(prompt.size());
|
||||
|
||||
prompt += "<|im_end|>\n<|im_start|>assistant\n";
|
||||
} else if (version == VERSION_FLUX2) {
|
||||
prompt_template_encode_start_idx = 0;
|
||||
out_layers = {10, 20, 30};
|
||||
|
||||
prompt = "[SYSTEM_PROMPT]You are an AI that reasons about image descriptions. You give structured responses focusing on object relationships, object\nattribution and actions without speculation.[/SYSTEM_PROMPT][INST]";
|
||||
|
||||
prompt_attn_range.first = static_cast<int>(prompt.size());
|
||||
prompt += conditioner_params.text;
|
||||
prompt_attn_range.second = static_cast<int>(prompt.size());
|
||||
|
||||
prompt += "[/INST]";
|
||||
} else if (sd_version_is_z_image(version)) {
|
||||
prompt_template_encode_start_idx = 0;
|
||||
out_layers = {35}; // -2
|
||||
|
||||
prompt = "<|im_start|>user\n";
|
||||
|
||||
prompt_attn_range.first = static_cast<int>(prompt.size());
|
||||
prompt += conditioner_params.text;
|
||||
prompt_attn_range.second = static_cast<int>(prompt.size());
|
||||
|
||||
prompt += "<|im_end|>\n<|im_start|>assistant\n";
|
||||
} else if (version == VERSION_FLUX2_KLEIN) {
|
||||
prompt_template_encode_start_idx = 0;
|
||||
max_length = 512;
|
||||
out_layers = {9, 18, 27};
|
||||
|
||||
prompt = "<|im_start|>user\n";
|
||||
|
||||
prompt_attn_range.first = static_cast<int>(prompt.size());
|
||||
prompt += conditioner_params.text;
|
||||
prompt_attn_range.second = static_cast<int>(prompt.size());
|
||||
|
||||
prompt += "<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n";
|
||||
|
||||
auto tokens_and_weights = tokenize(prompt, prompt_attn_range, 0, false);
|
||||
tokens = std::get<0>(tokens_and_weights);
|
||||
weights = std::get<1>(tokens_and_weights);
|
||||
|
||||
mask.insert(mask.end(), tokens.size(), 1.f);
|
||||
if (tokens.size() < max_length) {
|
||||
mask.insert(mask.end(), max_length - tokens.size(), 0.f);
|
||||
tokenizer->pad_tokens(tokens, weights, max_length, true);
|
||||
}
|
||||
} else if (version == VERSION_OVIS_IMAGE) {
|
||||
prompt_template_encode_start_idx = 28;
|
||||
max_length = prompt_template_encode_start_idx + 256;
|
||||
|
||||
prompt = "<|im_start|>user\nDescribe the image by detailing the color, quantity, text, shape, size, texture, spatial relationships of the objects and background:";
|
||||
|
||||
prompt_attn_range.first = static_cast<int>(prompt.size());
|
||||
prompt += " " + conditioner_params.text;
|
||||
prompt_attn_range.second = static_cast<int>(prompt.size());
|
||||
|
||||
prompt += "<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n";
|
||||
} else {
|
||||
prompt = "<|im_start|>system\nDescribe the image by detailing the color, shape, size, texture, quantity, text, spatial relationships of the objects and background:<|im_end|>\n<|im_start|>user\n" + conditioner_params.text + "<|im_end|>\n<|im_start|>assistant\n";
|
||||
prompt_template_encode_start_idx = 34;
|
||||
|
||||
prompt = "<|im_start|>system\nDescribe the image by detailing the color, shape, size, texture, quantity, text, spatial relationships of the objects and background:<|im_end|>\n<|im_start|>user\n";
|
||||
|
||||
prompt_attn_range.first = static_cast<int>(prompt.size());
|
||||
prompt += conditioner_params.text;
|
||||
prompt_attn_range.second = static_cast<int>(prompt.size());
|
||||
|
||||
prompt += "<|im_end|>\n<|im_start|>assistant\n";
|
||||
}
|
||||
|
||||
auto tokens_and_weights = tokenize(prompt, 0, system_prompt_length, false);
|
||||
auto& tokens = std::get<0>(tokens_and_weights);
|
||||
auto& weights = std::get<1>(tokens_and_weights);
|
||||
if (tokens.empty()) {
|
||||
auto tokens_and_weights = tokenize(prompt, prompt_attn_range, max_length, max_length > 0);
|
||||
tokens = std::get<0>(tokens_and_weights);
|
||||
weights = std::get<1>(tokens_and_weights);
|
||||
}
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
struct ggml_tensor* hidden_states = nullptr; // [N, n_token, 3584]
|
||||
|
||||
auto input_ids = vector_to_ggml_tensor_i32(work_ctx, tokens);
|
||||
|
||||
qwenvl->compute(n_threads,
|
||||
input_ids,
|
||||
image_embeds,
|
||||
&hidden_states,
|
||||
work_ctx);
|
||||
ggml_tensor* attention_mask = nullptr;
|
||||
if (!mask.empty()) {
|
||||
attention_mask = ggml_new_tensor_2d(work_ctx, GGML_TYPE_F32, mask.size(), mask.size());
|
||||
ggml_ext_tensor_iter(attention_mask, [&](ggml_tensor* attention_mask, int64_t i0, int64_t i1, int64_t i2, int64_t i3) {
|
||||
float value = 0.f;
|
||||
if (mask[i0] == 0.f) {
|
||||
value = -INFINITY;
|
||||
} else if (i0 > i1) {
|
||||
value = -INFINITY;
|
||||
}
|
||||
ggml_ext_tensor_set_f32(attention_mask, value, i0, i1, i2, i3);
|
||||
});
|
||||
}
|
||||
|
||||
llm->compute(n_threads,
|
||||
input_ids,
|
||||
attention_mask,
|
||||
image_embeds,
|
||||
out_layers,
|
||||
&hidden_states,
|
||||
work_ctx);
|
||||
{
|
||||
auto tensor = hidden_states;
|
||||
float original_mean = ggml_ext_tensor_mean(tensor);
|
||||
@@ -1739,17 +1930,34 @@ struct Qwen2_5_VLCLIPEmbedder : public Conditioner {
|
||||
|
||||
GGML_ASSERT(hidden_states->ne[1] > prompt_template_encode_start_idx);
|
||||
|
||||
int64_t min_length = 0;
|
||||
if (version == VERSION_FLUX2) {
|
||||
min_length = 512;
|
||||
}
|
||||
|
||||
int64_t zero_pad_len = 0;
|
||||
if (min_length > 0) {
|
||||
if (hidden_states->ne[1] - prompt_template_encode_start_idx < min_length) {
|
||||
zero_pad_len = min_length - hidden_states->ne[1] + prompt_template_encode_start_idx;
|
||||
}
|
||||
}
|
||||
|
||||
ggml_tensor* new_hidden_states = ggml_new_tensor_3d(work_ctx,
|
||||
GGML_TYPE_F32,
|
||||
hidden_states->ne[0],
|
||||
hidden_states->ne[1] - prompt_template_encode_start_idx,
|
||||
hidden_states->ne[1] - prompt_template_encode_start_idx + zero_pad_len,
|
||||
hidden_states->ne[2]);
|
||||
|
||||
ggml_ext_tensor_iter(new_hidden_states, [&](ggml_tensor* new_hidden_states, int64_t i0, int64_t i1, int64_t i2, int64_t i3) {
|
||||
float value = ggml_ext_tensor_get_f32(hidden_states, i0, i1 + prompt_template_encode_start_idx, i2, i3);
|
||||
float value = 0.f;
|
||||
if (i1 + prompt_template_encode_start_idx < hidden_states->ne[1]) {
|
||||
value = ggml_ext_tensor_get_f32(hidden_states, i0, i1 + prompt_template_encode_start_idx, i2, i3);
|
||||
}
|
||||
ggml_ext_tensor_set_f32(new_hidden_states, value, i0, i1, i2, i3);
|
||||
});
|
||||
|
||||
// print_ggml_tensor(new_hidden_states);
|
||||
|
||||
int64_t t1 = ggml_time_ms();
|
||||
LOG_DEBUG("computing condition graph completed, taking %" PRId64 " ms", t1 - t0);
|
||||
return {new_hidden_states, nullptr, nullptr};
|
||||
|
||||
@@ -380,7 +380,7 @@ struct ControlNet : public GGMLRunner {
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* y = nullptr) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph_custom(compute_ctx, CONTROL_NET_GRAPH_SIZE, false);
|
||||
struct ggml_cgraph* gf = new_graph_custom(CONTROL_NET_GRAPH_SIZE);
|
||||
|
||||
x = to_backend(x);
|
||||
if (guided_hint_cached) {
|
||||
@@ -414,7 +414,7 @@ struct ControlNet : public GGMLRunner {
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
bool compute(int n_threads,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* hint,
|
||||
struct ggml_tensor* timesteps,
|
||||
@@ -430,8 +430,12 @@ struct ControlNet : public GGMLRunner {
|
||||
return build_graph(x, hint, timesteps, context, y);
|
||||
};
|
||||
|
||||
GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
guided_hint_cached = true;
|
||||
bool res = GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
if (res) {
|
||||
// cache guided_hint
|
||||
guided_hint_cached = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef __DENOISER_HPP__
|
||||
#define __DENOISER_HPP__
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "ggml_extend.hpp"
|
||||
#include "gits_noise.inl"
|
||||
|
||||
@@ -11,14 +13,13 @@
|
||||
#define TIMESTEPS 1000
|
||||
#define FLUX_TIMESTEPS 1000
|
||||
|
||||
struct SigmaSchedule {
|
||||
int version = 0;
|
||||
struct SigmaScheduler {
|
||||
typedef std::function<float(float)> t_to_sigma_t;
|
||||
|
||||
virtual std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) = 0;
|
||||
};
|
||||
|
||||
struct DiscreteSchedule : SigmaSchedule {
|
||||
struct DiscreteScheduler : SigmaScheduler {
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) override {
|
||||
std::vector<float> result;
|
||||
|
||||
@@ -42,7 +43,7 @@ struct DiscreteSchedule : SigmaSchedule {
|
||||
}
|
||||
};
|
||||
|
||||
struct ExponentialSchedule : SigmaSchedule {
|
||||
struct ExponentialScheduler : SigmaScheduler {
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) override {
|
||||
std::vector<float> sigmas;
|
||||
|
||||
@@ -149,7 +150,10 @@ std::vector<float> log_linear_interpolation(std::vector<float> sigma_in,
|
||||
/*
|
||||
https://research.nvidia.com/labs/toronto-ai/AlignYourSteps/howto.html
|
||||
*/
|
||||
struct AYSSchedule : SigmaSchedule {
|
||||
struct AYSScheduler : SigmaScheduler {
|
||||
SDVersion version;
|
||||
explicit AYSScheduler(SDVersion version)
|
||||
: version(version) {}
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) override {
|
||||
const std::vector<float> noise_levels[] = {
|
||||
/* SD1.5 */
|
||||
@@ -169,19 +173,19 @@ struct AYSSchedule : SigmaSchedule {
|
||||
std::vector<float> results(n + 1);
|
||||
|
||||
if (sd_version_is_sd2((SDVersion)version)) {
|
||||
LOG_WARN("AYS not designed for SD2.X models");
|
||||
LOG_WARN("AYS_SCHEDULER not designed for SD2.X models");
|
||||
} /* fallthrough */
|
||||
else if (sd_version_is_sd1((SDVersion)version)) {
|
||||
LOG_INFO("AYS using SD1.5 noise levels");
|
||||
LOG_INFO("AYS_SCHEDULER using SD1.5 noise levels");
|
||||
inputs = noise_levels[0];
|
||||
} else if (sd_version_is_sdxl((SDVersion)version)) {
|
||||
LOG_INFO("AYS using SDXL noise levels");
|
||||
LOG_INFO("AYS_SCHEDULER using SDXL noise levels");
|
||||
inputs = noise_levels[1];
|
||||
} else if (version == VERSION_SVD) {
|
||||
LOG_INFO("AYS using SVD noise levels");
|
||||
LOG_INFO("AYS_SCHEDULER using SVD noise levels");
|
||||
inputs = noise_levels[2];
|
||||
} else {
|
||||
LOG_ERROR("Version not compatible with AYS scheduler");
|
||||
LOG_ERROR("Version not compatible with AYS_SCHEDULER scheduler");
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -203,7 +207,7 @@ struct AYSSchedule : SigmaSchedule {
|
||||
/*
|
||||
* GITS Scheduler: https://github.com/zju-pi/diff-sampler/tree/main/gits-main
|
||||
*/
|
||||
struct GITSSchedule : SigmaSchedule {
|
||||
struct GITSScheduler : SigmaScheduler {
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) override {
|
||||
if (sigma_max <= 0.0f) {
|
||||
return std::vector<float>{};
|
||||
@@ -232,7 +236,7 @@ struct GITSSchedule : SigmaSchedule {
|
||||
}
|
||||
};
|
||||
|
||||
struct SGMUniformSchedule : SigmaSchedule {
|
||||
struct SGMUniformScheduler : SigmaScheduler {
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min_in, float sigma_max_in, t_to_sigma_t t_to_sigma_func) override {
|
||||
std::vector<float> result;
|
||||
if (n == 0) {
|
||||
@@ -243,7 +247,7 @@ struct SGMUniformSchedule : SigmaSchedule {
|
||||
int t_max = TIMESTEPS - 1;
|
||||
int t_min = 0;
|
||||
std::vector<float> timesteps = linear_space(static_cast<float>(t_max), static_cast<float>(t_min), n + 1);
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
result.push_back(t_to_sigma_func(timesteps[i]));
|
||||
}
|
||||
result.push_back(0.0f);
|
||||
@@ -251,12 +255,33 @@ struct SGMUniformSchedule : SigmaSchedule {
|
||||
}
|
||||
};
|
||||
|
||||
struct KarrasSchedule : SigmaSchedule {
|
||||
struct LCMScheduler : SigmaScheduler {
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) override {
|
||||
std::vector<float> result;
|
||||
result.reserve(n + 1);
|
||||
const int original_steps = 50;
|
||||
const int k = TIMESTEPS / original_steps;
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
// the rounding ensures we match the training schedule of the LCM model
|
||||
int index = (i * original_steps) / n;
|
||||
int timestep = (original_steps - index) * k - 1;
|
||||
result.push_back(t_to_sigma(static_cast<float>(timestep)));
|
||||
}
|
||||
result.push_back(0.0f);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct KarrasScheduler : SigmaScheduler {
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) override {
|
||||
// These *COULD* be function arguments here,
|
||||
// but does anybody ever bother to touch them?
|
||||
float rho = 7.f;
|
||||
|
||||
if (sigma_min <= 1e-6f) {
|
||||
sigma_min = 1e-6f;
|
||||
}
|
||||
|
||||
std::vector<float> result(n + 1);
|
||||
|
||||
float min_inv_rho = pow(sigma_min, (1.f / rho));
|
||||
@@ -270,7 +295,7 @@ struct KarrasSchedule : SigmaSchedule {
|
||||
}
|
||||
};
|
||||
|
||||
struct SimpleSchedule : SigmaSchedule {
|
||||
struct SimpleScheduler : SigmaScheduler {
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) override {
|
||||
std::vector<float> result_sigmas;
|
||||
|
||||
@@ -299,8 +324,8 @@ struct SimpleSchedule : SigmaSchedule {
|
||||
}
|
||||
};
|
||||
|
||||
// Close to Beta Schedule, but increadably simple in code.
|
||||
struct SmoothStepSchedule : SigmaSchedule {
|
||||
// Close to Beta Scheduler, but increadably simple in code.
|
||||
struct SmoothStepScheduler : SigmaScheduler {
|
||||
static constexpr float smoothstep(float x) {
|
||||
return x * x * (3.0f - 2.0f * x);
|
||||
}
|
||||
@@ -328,8 +353,131 @@ struct SmoothStepSchedule : SigmaSchedule {
|
||||
}
|
||||
};
|
||||
|
||||
struct BongTangentScheduler : SigmaScheduler {
|
||||
static constexpr float kPi = 3.14159265358979323846f;
|
||||
|
||||
static std::vector<float> get_bong_tangent_sigmas(int steps, float slope, float pivot, float start, float end) {
|
||||
std::vector<float> sigmas;
|
||||
if (steps <= 0) {
|
||||
return sigmas;
|
||||
}
|
||||
|
||||
float smax = ((2.0f / kPi) * atanf(-slope * (0.0f - pivot)) + 1.0f) * 0.5f;
|
||||
float smin = ((2.0f / kPi) * atanf(-slope * ((float)(steps - 1) - pivot)) + 1.0f) * 0.5f;
|
||||
float srange = smax - smin;
|
||||
float sscale = start - end;
|
||||
|
||||
sigmas.reserve(steps);
|
||||
|
||||
if (fabsf(srange) < 1e-8f) {
|
||||
if (steps == 1) {
|
||||
sigmas.push_back(start);
|
||||
return sigmas;
|
||||
}
|
||||
for (int i = 0; i < steps; ++i) {
|
||||
float t = (float)i / (float)(steps - 1);
|
||||
sigmas.push_back(start + (end - start) * t);
|
||||
}
|
||||
return sigmas;
|
||||
}
|
||||
|
||||
float inv_srange = 1.0f / srange;
|
||||
for (int x = 0; x < steps; ++x) {
|
||||
float v = ((2.0f / kPi) * atanf(-slope * ((float)x - pivot)) + 1.0f) * 0.5f;
|
||||
float sigma = ((v - smin) * inv_srange) * sscale + end;
|
||||
sigmas.push_back(sigma);
|
||||
}
|
||||
|
||||
return sigmas;
|
||||
}
|
||||
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t /*t_to_sigma*/) override {
|
||||
std::vector<float> result;
|
||||
if (n == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
float start = sigma_max;
|
||||
float end = sigma_min;
|
||||
float middle = sigma_min + (sigma_max - sigma_min) * 0.5f;
|
||||
|
||||
float pivot_1 = 0.6f;
|
||||
float pivot_2 = 0.6f;
|
||||
float slope_1 = 0.2f;
|
||||
float slope_2 = 0.2f;
|
||||
|
||||
int steps = static_cast<int>(n) + 2;
|
||||
int midpoint = static_cast<int>(((float)steps * pivot_1 + (float)steps * pivot_2) * 0.5f);
|
||||
int pivot_1_i = static_cast<int>((float)steps * pivot_1);
|
||||
int pivot_2_i = static_cast<int>((float)steps * pivot_2);
|
||||
|
||||
float slope_scale = (float)steps / 40.0f;
|
||||
slope_1 = slope_1 / slope_scale;
|
||||
slope_2 = slope_2 / slope_scale;
|
||||
|
||||
int stage_2_len = steps - midpoint;
|
||||
int stage_1_len = steps - stage_2_len;
|
||||
|
||||
std::vector<float> sigmas_1 = get_bong_tangent_sigmas(stage_1_len, slope_1, (float)pivot_1_i, start, middle);
|
||||
std::vector<float> sigmas_2 = get_bong_tangent_sigmas(stage_2_len, slope_2, (float)(pivot_2_i - stage_1_len), middle, end);
|
||||
|
||||
if (!sigmas_1.empty()) {
|
||||
sigmas_1.pop_back();
|
||||
}
|
||||
|
||||
result.reserve(n + 1);
|
||||
result.insert(result.end(), sigmas_1.begin(), sigmas_1.end());
|
||||
result.insert(result.end(), sigmas_2.begin(), sigmas_2.end());
|
||||
|
||||
if (result.size() < n + 1) {
|
||||
while (result.size() < n + 1) {
|
||||
result.push_back(end);
|
||||
}
|
||||
} else if (result.size() > n + 1) {
|
||||
result.resize(n + 1);
|
||||
}
|
||||
|
||||
result[n] = 0.0f;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct KLOptimalScheduler : SigmaScheduler {
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) override {
|
||||
std::vector<float> sigmas;
|
||||
|
||||
if (n == 0) {
|
||||
return sigmas;
|
||||
}
|
||||
|
||||
if (n == 1) {
|
||||
sigmas.push_back(sigma_max);
|
||||
sigmas.push_back(0.0f);
|
||||
return sigmas;
|
||||
}
|
||||
|
||||
if (sigma_min <= 1e-6f) {
|
||||
sigma_min = 1e-6f;
|
||||
}
|
||||
|
||||
sigmas.reserve(n + 1);
|
||||
|
||||
float alpha_min = std::atan(sigma_min);
|
||||
float alpha_max = std::atan(sigma_max);
|
||||
|
||||
for (uint32_t i = 0; i < n; ++i) {
|
||||
float t = static_cast<float>(i) / static_cast<float>(n - 1);
|
||||
float angle = t * alpha_min + (1.0f - t) * alpha_max;
|
||||
sigmas.push_back(std::tan(angle));
|
||||
}
|
||||
|
||||
sigmas.push_back(0.0f);
|
||||
|
||||
return sigmas;
|
||||
}
|
||||
};
|
||||
|
||||
struct Denoiser {
|
||||
std::shared_ptr<SigmaSchedule> scheduler = std::make_shared<DiscreteSchedule>();
|
||||
virtual float sigma_min() = 0;
|
||||
virtual float sigma_max() = 0;
|
||||
virtual float sigma_to_t(float sigma) = 0;
|
||||
@@ -338,8 +486,59 @@ struct Denoiser {
|
||||
virtual ggml_tensor* noise_scaling(float sigma, ggml_tensor* noise, ggml_tensor* latent) = 0;
|
||||
virtual ggml_tensor* inverse_noise_scaling(float sigma, ggml_tensor* latent) = 0;
|
||||
|
||||
virtual std::vector<float> get_sigmas(uint32_t n) {
|
||||
virtual std::vector<float> get_sigmas(uint32_t n, int /*image_seq_len*/, scheduler_t scheduler_type, SDVersion version) {
|
||||
auto bound_t_to_sigma = std::bind(&Denoiser::t_to_sigma, this, std::placeholders::_1);
|
||||
std::shared_ptr<SigmaScheduler> scheduler;
|
||||
switch (scheduler_type) {
|
||||
case DISCRETE_SCHEDULER:
|
||||
LOG_INFO("get_sigmas with discrete scheduler");
|
||||
scheduler = std::make_shared<DiscreteScheduler>();
|
||||
break;
|
||||
case KARRAS_SCHEDULER:
|
||||
LOG_INFO("get_sigmas with Karras scheduler");
|
||||
scheduler = std::make_shared<KarrasScheduler>();
|
||||
break;
|
||||
case EXPONENTIAL_SCHEDULER:
|
||||
LOG_INFO("get_sigmas exponential scheduler");
|
||||
scheduler = std::make_shared<ExponentialScheduler>();
|
||||
break;
|
||||
case AYS_SCHEDULER:
|
||||
LOG_INFO("get_sigmas with Align-Your-Steps scheduler");
|
||||
scheduler = std::make_shared<AYSScheduler>(version);
|
||||
break;
|
||||
case GITS_SCHEDULER:
|
||||
LOG_INFO("get_sigmas with GITS scheduler");
|
||||
scheduler = std::make_shared<GITSScheduler>();
|
||||
break;
|
||||
case SGM_UNIFORM_SCHEDULER:
|
||||
LOG_INFO("get_sigmas with SGM Uniform scheduler");
|
||||
scheduler = std::make_shared<SGMUniformScheduler>();
|
||||
break;
|
||||
case SIMPLE_SCHEDULER:
|
||||
LOG_INFO("get_sigmas with Simple scheduler");
|
||||
scheduler = std::make_shared<SimpleScheduler>();
|
||||
break;
|
||||
case SMOOTHSTEP_SCHEDULER:
|
||||
LOG_INFO("get_sigmas with SmoothStep scheduler");
|
||||
scheduler = std::make_shared<SmoothStepScheduler>();
|
||||
break;
|
||||
case BONG_TANGENT_SCHEDULER:
|
||||
LOG_INFO("get_sigmas with bong_tangent scheduler");
|
||||
scheduler = std::make_shared<BongTangentScheduler>();
|
||||
break;
|
||||
case KL_OPTIMAL_SCHEDULER:
|
||||
LOG_INFO("get_sigmas with KL Optimal scheduler");
|
||||
scheduler = std::make_shared<KLOptimalScheduler>();
|
||||
break;
|
||||
case LCM_SCHEDULER:
|
||||
LOG_INFO("get_sigmas with LCM scheduler");
|
||||
scheduler = std::make_shared<LCMScheduler>();
|
||||
break;
|
||||
default:
|
||||
LOG_INFO("get_sigmas with discrete scheduler (default)");
|
||||
scheduler = std::make_shared<DiscreteScheduler>();
|
||||
break;
|
||||
}
|
||||
return scheduler->get_sigmas(n, sigma_min(), sigma_max(), bound_t_to_sigma);
|
||||
}
|
||||
};
|
||||
@@ -421,12 +620,11 @@ struct CompVisVDenoiser : public CompVisDenoiser {
|
||||
};
|
||||
|
||||
struct EDMVDenoiser : public CompVisVDenoiser {
|
||||
float min_sigma = 0.002;
|
||||
float max_sigma = 120.0;
|
||||
float min_sigma = 0.002f;
|
||||
float max_sigma = 120.0f;
|
||||
|
||||
EDMVDenoiser(float min_sigma = 0.002, float max_sigma = 120.0)
|
||||
: min_sigma(min_sigma), max_sigma(max_sigma) {
|
||||
scheduler = std::make_shared<ExponentialSchedule>();
|
||||
}
|
||||
|
||||
float t_to_sigma(float t) override {
|
||||
@@ -434,7 +632,7 @@ struct EDMVDenoiser : public CompVisVDenoiser {
|
||||
}
|
||||
|
||||
float sigma_to_t(float s) override {
|
||||
return 0.25 * std::log(s);
|
||||
return 0.25f * std::log(s);
|
||||
}
|
||||
|
||||
float sigma_min() override {
|
||||
@@ -466,7 +664,7 @@ struct DiscreteFlowDenoiser : public Denoiser {
|
||||
|
||||
void set_parameters() {
|
||||
for (int i = 1; i < TIMESTEPS + 1; i++) {
|
||||
sigmas[i - 1] = t_to_sigma(i);
|
||||
sigmas[i - 1] = t_to_sigma(static_cast<float>(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,7 +707,7 @@ struct DiscreteFlowDenoiser : public Denoiser {
|
||||
};
|
||||
|
||||
float flux_time_shift(float mu, float sigma, float t) {
|
||||
return std::exp(mu) / (std::exp(mu) + std::pow((1.0 / t - 1.0), sigma));
|
||||
return ::expf(mu) / (::expf(mu) + ::powf((1.0f / t - 1.0f), sigma));
|
||||
}
|
||||
|
||||
struct FluxFlowDenoiser : public Denoiser {
|
||||
@@ -522,10 +720,14 @@ struct FluxFlowDenoiser : public Denoiser {
|
||||
set_parameters(shift);
|
||||
}
|
||||
|
||||
void set_parameters(float shift = 1.15f) {
|
||||
void set_shift(float shift) {
|
||||
this->shift = shift;
|
||||
for (int i = 1; i < TIMESTEPS + 1; i++) {
|
||||
sigmas[i - 1] = t_to_sigma(i / TIMESTEPS * TIMESTEPS);
|
||||
}
|
||||
|
||||
void set_parameters(float shift) {
|
||||
set_shift(shift);
|
||||
for (int i = 0; i < TIMESTEPS; i++) {
|
||||
sigmas[i] = t_to_sigma(static_cast<float>(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,10 +769,42 @@ struct FluxFlowDenoiser : public Denoiser {
|
||||
}
|
||||
};
|
||||
|
||||
struct Flux2FlowDenoiser : public FluxFlowDenoiser {
|
||||
Flux2FlowDenoiser() = default;
|
||||
|
||||
float compute_empirical_mu(uint32_t n, int image_seq_len) {
|
||||
const float a1 = 8.73809524e-05f;
|
||||
const float b1 = 1.89833333f;
|
||||
const float a2 = 0.00016927f;
|
||||
const float b2 = 0.45666666f;
|
||||
|
||||
if (image_seq_len > 4300) {
|
||||
float mu = a2 * image_seq_len + b2;
|
||||
return mu;
|
||||
}
|
||||
|
||||
float m_200 = a2 * image_seq_len + b2;
|
||||
float m_10 = a1 * image_seq_len + b1;
|
||||
|
||||
float a = (m_200 - m_10) / 190.0f;
|
||||
float b = m_200 - 200.0f * a;
|
||||
float mu = a * n + b;
|
||||
|
||||
return mu;
|
||||
}
|
||||
|
||||
std::vector<float> get_sigmas(uint32_t n, int image_seq_len, scheduler_t scheduler_type, SDVersion version) override {
|
||||
float mu = compute_empirical_mu(n, image_seq_len);
|
||||
LOG_DEBUG("Flux2FlowDenoiser: set shift to %.3f", mu);
|
||||
set_shift(mu);
|
||||
return Denoiser::get_sigmas(n, image_seq_len, scheduler_type, version);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::function<ggml_tensor*(ggml_tensor*, float, int)> denoise_cb_t;
|
||||
|
||||
// k diffusion reverse ODE: dx = (x - D(x;\sigma)) / \sigma dt; \sigma(t) = t
|
||||
static void sample_k_diffusion(sample_method_t method,
|
||||
static bool sample_k_diffusion(sample_method_t method,
|
||||
denoise_cb_t model,
|
||||
ggml_context* work_ctx,
|
||||
ggml_tensor* x,
|
||||
@@ -580,7 +814,7 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
size_t steps = sigmas.size() - 1;
|
||||
// sample_euler_ancestral
|
||||
switch (method) {
|
||||
case EULER_A: {
|
||||
case EULER_A_SAMPLE_METHOD: {
|
||||
struct ggml_tensor* noise = ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* d = ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
@@ -589,6 +823,9 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
|
||||
// denoise
|
||||
ggml_tensor* denoised = model(x, sigma, i + 1);
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// d = (x - denoised) / sigma
|
||||
{
|
||||
@@ -633,7 +870,7 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case EULER: // Implemented without any sigma churn
|
||||
case EULER_SAMPLE_METHOD: // Implemented without any sigma churn
|
||||
{
|
||||
struct ggml_tensor* d = ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
@@ -642,6 +879,9 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
|
||||
// denoise
|
||||
ggml_tensor* denoised = model(x, sigma, i + 1);
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// d = (x - denoised) / sigma
|
||||
{
|
||||
@@ -666,13 +906,16 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case HEUN: {
|
||||
case HEUN_SAMPLE_METHOD: {
|
||||
struct ggml_tensor* d = ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* x2 = ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
// denoise
|
||||
ggml_tensor* denoised = model(x, sigmas[i], -(i + 1));
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// d = (x - denoised) / sigma
|
||||
{
|
||||
@@ -707,7 +950,10 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
|
||||
ggml_tensor* denoised = model(x2, sigmas[i + 1], i + 1);
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
float d2 = (vec_x2[j] - vec_denoised[j]) / sigmas[i + 1];
|
||||
vec_d[j] = (vec_d[j] + d2) / 2;
|
||||
@@ -716,13 +962,16 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case DPM2: {
|
||||
case DPM2_SAMPLE_METHOD: {
|
||||
struct ggml_tensor* d = ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* x2 = ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
// denoise
|
||||
ggml_tensor* denoised = model(x, sigmas[i], i + 1);
|
||||
ggml_tensor* denoised = model(x, sigmas[i], -(i + 1));
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// d = (x - denoised) / sigma
|
||||
{
|
||||
@@ -759,7 +1008,10 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
|
||||
ggml_tensor* denoised = model(x2, sigma_mid, i + 1);
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
float d2 = (vec_x2[j] - vec_denoised[j]) / sigma_mid;
|
||||
vec_x[j] = vec_x[j] + d2 * dt_2;
|
||||
@@ -768,13 +1020,16 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
|
||||
} break;
|
||||
case DPMPP2S_A: {
|
||||
case DPMPP2S_A_SAMPLE_METHOD: {
|
||||
struct ggml_tensor* noise = ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* x2 = ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
// denoise
|
||||
ggml_tensor* denoised = model(x, sigmas[i], i + 1);
|
||||
ggml_tensor* denoised = model(x, sigmas[i], -(i + 1));
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get_ancestral_step
|
||||
float sigma_up = std::min(sigmas[i + 1],
|
||||
@@ -811,6 +1066,9 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
|
||||
ggml_tensor* denoised = model(x2, sigmas[i + 1], i + 1);
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Second half-step
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
@@ -832,7 +1090,7 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case DPMPP2M: // DPM++ (2M) from Karras et al (2022)
|
||||
case DPMPP2M_SAMPLE_METHOD: // DPM++ (2M) from Karras et al (2022)
|
||||
{
|
||||
struct ggml_tensor* old_denoised = ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
@@ -841,6 +1099,9 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
for (int i = 0; i < steps; i++) {
|
||||
// denoise
|
||||
ggml_tensor* denoised = model(x, sigmas[i], i + 1);
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float t = t_fn(sigmas[i]);
|
||||
float t_next = t_fn(sigmas[i + 1]);
|
||||
@@ -871,7 +1132,7 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case DPMPP2Mv2: // Modified DPM++ (2M) from https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/8457
|
||||
case DPMPP2Mv2_SAMPLE_METHOD: // Modified DPM++ (2M) from https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/8457
|
||||
{
|
||||
struct ggml_tensor* old_denoised = ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
@@ -880,6 +1141,9 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
for (int i = 0; i < steps; i++) {
|
||||
// denoise
|
||||
ggml_tensor* denoised = model(x, sigmas[i], i + 1);
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float t = t_fn(sigmas[i]);
|
||||
float t_next = t_fn(sigmas[i + 1]);
|
||||
@@ -914,7 +1178,7 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case IPNDM: // iPNDM sampler from https://github.com/zju-pi/diff-sampler/tree/main/diff-solvers-main
|
||||
case IPNDM_SAMPLE_METHOD: // iPNDM sampler from https://github.com/zju-pi/diff-sampler/tree/main/diff-solvers-main
|
||||
{
|
||||
int max_order = 4;
|
||||
ggml_tensor* x_next = x;
|
||||
@@ -930,7 +1194,10 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
|
||||
// Denoising step
|
||||
ggml_tensor* denoised = model(x_cur, sigma, i + 1);
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
// d_cur = (x_cur - denoised) / sigma
|
||||
struct ggml_tensor* d_cur = ggml_dup_tensor(work_ctx, x_cur);
|
||||
float* vec_d_cur = (float*)d_cur->data;
|
||||
@@ -989,7 +1256,7 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case IPNDM_V: // iPNDM_v sampler from https://github.com/zju-pi/diff-sampler/tree/main/diff-solvers-main
|
||||
case IPNDM_V_SAMPLE_METHOD: // iPNDM_v sampler from https://github.com/zju-pi/diff-sampler/tree/main/diff-solvers-main
|
||||
{
|
||||
int max_order = 4;
|
||||
std::vector<ggml_tensor*> buffer_model;
|
||||
@@ -1063,7 +1330,7 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
d_cur = ggml_dup_tensor(work_ctx, x_next);
|
||||
}
|
||||
} break;
|
||||
case LCM: // Latent Consistency Models
|
||||
case LCM_SAMPLE_METHOD: // Latent Consistency Models
|
||||
{
|
||||
struct ggml_tensor* noise = ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* d = ggml_dup_tensor(work_ctx, x);
|
||||
@@ -1073,6 +1340,9 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
|
||||
// denoise
|
||||
ggml_tensor* denoised = model(x, sigma, i + 1);
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// x = denoised
|
||||
{
|
||||
@@ -1098,8 +1368,8 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case DDIM_TRAILING: // Denoising Diffusion Implicit Models
|
||||
// with the "trailing" timestep spacing
|
||||
case DDIM_TRAILING_SAMPLE_METHOD: // Denoising Diffusion Implicit Models
|
||||
// with the "trailing" timestep spacing
|
||||
{
|
||||
// See J. Song et al., "Denoising Diffusion Implicit
|
||||
// Models", arXiv:2010.02502 [cs.LG]
|
||||
@@ -1109,7 +1379,7 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
// end beta) (which unfortunately k-diffusion's data
|
||||
// structure hides from the denoiser), and the sigmas are
|
||||
// also needed to invert the behavior of CompVisDenoiser
|
||||
// (k-diffusion's LMSDiscreteScheduler)
|
||||
// (k-diffusion's LMSDiscreteSchedulerr)
|
||||
float beta_start = 0.00085f;
|
||||
float beta_end = 0.0120f;
|
||||
std::vector<double> alphas_cumprod;
|
||||
@@ -1137,7 +1407,7 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
// The "trailing" DDIM timestep, see S. Lin et al.,
|
||||
// "Common Diffusion Noise Schedules and Sample Steps
|
||||
// "Common Diffusion Noise Schedulers and Sample Steps
|
||||
// are Flawed", arXiv:2305.08891 [cs], p. 4, Table
|
||||
// 2. Most variables below follow Diffusers naming
|
||||
//
|
||||
@@ -1152,15 +1422,12 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
// - pred_sample_direction -> "direction pointing to
|
||||
// x_t"
|
||||
// - pred_prev_sample -> "x_t-1"
|
||||
int timestep =
|
||||
roundf(TIMESTEPS -
|
||||
i * ((float)TIMESTEPS / steps)) -
|
||||
1;
|
||||
int timestep = static_cast<int>(roundf(TIMESTEPS - i * ((float)TIMESTEPS / steps))) - 1;
|
||||
// 1. get previous step value (=t-1)
|
||||
int prev_timestep = timestep - TIMESTEPS / steps;
|
||||
int prev_timestep = timestep - TIMESTEPS / static_cast<int>(steps);
|
||||
// The sigma here is chosen to cause the
|
||||
// CompVisDenoiser to produce t = timestep
|
||||
float sigma = compvis_sigmas[timestep];
|
||||
float sigma = static_cast<float>(compvis_sigmas[timestep]);
|
||||
if (i == 0) {
|
||||
// The function add_noise intializes x to
|
||||
// Diffusers' latents * sigma (as in Diffusers'
|
||||
@@ -1217,10 +1484,10 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
// 2. compute alphas, betas
|
||||
float alpha_prod_t = alphas_cumprod[timestep];
|
||||
float alpha_prod_t = static_cast<float>(alphas_cumprod[timestep]);
|
||||
// Note final_alpha_cumprod = alphas_cumprod[0] due to
|
||||
// trailing timestep spacing
|
||||
float alpha_prod_t_prev = prev_timestep >= 0 ? alphas_cumprod[prev_timestep] : alphas_cumprod[0];
|
||||
float alpha_prod_t_prev = static_cast<float>(prev_timestep >= 0 ? alphas_cumprod[prev_timestep] : alphas_cumprod[0]);
|
||||
float beta_prod_t = 1 - alpha_prod_t;
|
||||
// 3. compute predicted original sample from predicted
|
||||
// noise also called "predicted x_0" of formula (12)
|
||||
@@ -1267,8 +1534,8 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
// Two step inner loop without an explicit
|
||||
// tensor
|
||||
float pred_sample_direction =
|
||||
std::sqrt(1 - alpha_prod_t_prev -
|
||||
std::pow(std_dev_t, 2)) *
|
||||
::sqrtf(1 - alpha_prod_t_prev -
|
||||
::powf(std_dev_t, 2)) *
|
||||
vec_model_output[j];
|
||||
vec_x[j] = std::sqrt(alpha_prod_t_prev) *
|
||||
vec_pred_original_sample[j] +
|
||||
@@ -1292,8 +1559,8 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
// factor c_in.
|
||||
}
|
||||
} break;
|
||||
case TCD: // Strategic Stochastic Sampling (Algorithm 4) in
|
||||
// Trajectory Consistency Distillation
|
||||
case TCD_SAMPLE_METHOD: // Strategic Stochastic Sampling (Algorithm 4) in
|
||||
// Trajectory Consistency Distillation
|
||||
{
|
||||
// See J. Zheng et al., "Trajectory Consistency
|
||||
// Distillation: Improved Latent Consistency Distillation
|
||||
@@ -1343,7 +1610,7 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
// Begin k-diffusion specific workaround for
|
||||
// evaluating F_theta(x; ...) from D(x, sigma), same
|
||||
// as in DDIM (and see there for detailed comments)
|
||||
float sigma = compvis_sigmas[timestep];
|
||||
float sigma = static_cast<float>(compvis_sigmas[timestep]);
|
||||
if (i == 0) {
|
||||
float* vec_x = (float*)x->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
@@ -1382,14 +1649,14 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
// is different from the notation alpha_t in
|
||||
// DPM-Solver. In fact, we have alpha_{t_n} =
|
||||
// \sqrt{\hat{alpha_n}}, [...]"
|
||||
float alpha_prod_t = alphas_cumprod[timestep];
|
||||
float alpha_prod_t = static_cast<float>(alphas_cumprod[timestep]);
|
||||
float beta_prod_t = 1 - alpha_prod_t;
|
||||
// Note final_alpha_cumprod = alphas_cumprod[0] since
|
||||
// TCD is always "trailing"
|
||||
float alpha_prod_t_prev = prev_timestep >= 0 ? alphas_cumprod[prev_timestep] : alphas_cumprod[0];
|
||||
float alpha_prod_t_prev = static_cast<float>(prev_timestep >= 0 ? alphas_cumprod[prev_timestep] : alphas_cumprod[0]);
|
||||
// The subscript _s are the only portion in this
|
||||
// section (2) unique to TCD
|
||||
float alpha_prod_s = alphas_cumprod[timestep_s];
|
||||
float alpha_prod_s = static_cast<float>(alphas_cumprod[timestep_s]);
|
||||
float beta_prod_s = 1 - alpha_prod_s;
|
||||
// 3. Compute the predicted noised sample x_s based on
|
||||
// the model parameterization
|
||||
@@ -1462,11 +1729,222 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case RES_MULTISTEP_SAMPLE_METHOD: // Res Multistep sampler
|
||||
{
|
||||
struct ggml_tensor* noise = ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* old_denoised = ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
bool have_old_sigma = false;
|
||||
float old_sigma_down = 0.0f;
|
||||
|
||||
auto t_fn = [](float sigma) -> float { return -logf(sigma); };
|
||||
auto sigma_fn = [](float t) -> float { return expf(-t); };
|
||||
auto phi1_fn = [](float t) -> float {
|
||||
if (fabsf(t) < 1e-6f) {
|
||||
return 1.0f + t * 0.5f + (t * t) / 6.0f;
|
||||
}
|
||||
return (expf(t) - 1.0f) / t;
|
||||
};
|
||||
auto phi2_fn = [&](float t) -> float {
|
||||
if (fabsf(t) < 1e-6f) {
|
||||
return 0.5f + t / 6.0f + (t * t) / 24.0f;
|
||||
}
|
||||
float phi1_val = phi1_fn(t);
|
||||
return (phi1_val - 1.0f) / t;
|
||||
};
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
ggml_tensor* denoised = model(x, sigmas[i], i + 1);
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float sigma_from = sigmas[i];
|
||||
float sigma_to = sigmas[i + 1];
|
||||
float sigma_up = 0.0f;
|
||||
float sigma_down = sigma_to;
|
||||
|
||||
if (eta > 0.0f) {
|
||||
float sigma_from_sq = sigma_from * sigma_from;
|
||||
float sigma_to_sq = sigma_to * sigma_to;
|
||||
if (sigma_from_sq > 0.0f) {
|
||||
float term = sigma_to_sq * (sigma_from_sq - sigma_to_sq) / sigma_from_sq;
|
||||
if (term > 0.0f) {
|
||||
sigma_up = eta * std::sqrt(term);
|
||||
}
|
||||
}
|
||||
sigma_up = std::min(sigma_up, sigma_to);
|
||||
float sigma_down_sq = sigma_to_sq - sigma_up * sigma_up;
|
||||
sigma_down = sigma_down_sq > 0.0f ? std::sqrt(sigma_down_sq) : 0.0f;
|
||||
}
|
||||
|
||||
if (sigma_down == 0.0f || !have_old_sigma) {
|
||||
float dt = sigma_down - sigma_from;
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
float d = (vec_x[j] - vec_denoised[j]) / sigma_from;
|
||||
vec_x[j] = vec_x[j] + d * dt;
|
||||
}
|
||||
} else {
|
||||
float t = t_fn(sigma_from);
|
||||
float t_old = t_fn(old_sigma_down);
|
||||
float t_next = t_fn(sigma_down);
|
||||
float t_prev = t_fn(sigmas[i - 1]);
|
||||
float h = t_next - t;
|
||||
float c2 = (t_prev - t_old) / h;
|
||||
|
||||
float phi1_val = phi1_fn(-h);
|
||||
float phi2_val = phi2_fn(-h);
|
||||
float b1 = phi1_val - phi2_val / c2;
|
||||
float b2 = phi2_val / c2;
|
||||
|
||||
if (!std::isfinite(b1)) {
|
||||
b1 = 0.0f;
|
||||
}
|
||||
if (!std::isfinite(b2)) {
|
||||
b2 = 0.0f;
|
||||
}
|
||||
|
||||
float sigma_h = sigma_fn(h);
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
float* vec_old_denoised = (float*)old_denoised->data;
|
||||
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x[j] = sigma_h * vec_x[j] + h * (b1 * vec_denoised[j] + b2 * vec_old_denoised[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (sigmas[i + 1] > 0 && sigma_up > 0.0f) {
|
||||
ggml_ext_im_set_randn_f32(noise, rng);
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_noise = (float*)noise->data;
|
||||
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x[j] = vec_x[j] + vec_noise[j] * sigma_up;
|
||||
}
|
||||
}
|
||||
|
||||
float* vec_old_denoised = (float*)old_denoised->data;
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_old_denoised[j] = vec_denoised[j];
|
||||
}
|
||||
|
||||
old_sigma_down = sigma_down;
|
||||
have_old_sigma = true;
|
||||
}
|
||||
} break;
|
||||
case RES_2S_SAMPLE_METHOD: // Res 2s sampler
|
||||
{
|
||||
struct ggml_tensor* noise = ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* x0 = ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* x2 = ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
const float c2 = 0.5f;
|
||||
auto t_fn = [](float sigma) -> float { return -logf(sigma); };
|
||||
auto phi1_fn = [](float t) -> float {
|
||||
if (fabsf(t) < 1e-6f) {
|
||||
return 1.0f + t * 0.5f + (t * t) / 6.0f;
|
||||
}
|
||||
return (expf(t) - 1.0f) / t;
|
||||
};
|
||||
auto phi2_fn = [&](float t) -> float {
|
||||
if (fabsf(t) < 1e-6f) {
|
||||
return 0.5f + t / 6.0f + (t * t) / 24.0f;
|
||||
}
|
||||
float phi1_val = phi1_fn(t);
|
||||
return (phi1_val - 1.0f) / t;
|
||||
};
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
float sigma_from = sigmas[i];
|
||||
float sigma_to = sigmas[i + 1];
|
||||
|
||||
ggml_tensor* denoised = model(x, sigma_from, -(i + 1));
|
||||
if (denoised == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float sigma_up = 0.0f;
|
||||
float sigma_down = sigma_to;
|
||||
if (eta > 0.0f) {
|
||||
float sigma_from_sq = sigma_from * sigma_from;
|
||||
float sigma_to_sq = sigma_to * sigma_to;
|
||||
if (sigma_from_sq > 0.0f) {
|
||||
float term = sigma_to_sq * (sigma_from_sq - sigma_to_sq) / sigma_from_sq;
|
||||
if (term > 0.0f) {
|
||||
sigma_up = eta * std::sqrt(term);
|
||||
}
|
||||
}
|
||||
sigma_up = std::min(sigma_up, sigma_to);
|
||||
float sigma_down_sq = sigma_to_sq - sigma_up * sigma_up;
|
||||
sigma_down = sigma_down_sq > 0.0f ? std::sqrt(sigma_down_sq) : 0.0f;
|
||||
}
|
||||
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_x0 = (float*)x0->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x0[j] = vec_x[j];
|
||||
}
|
||||
|
||||
if (sigma_down == 0.0f || sigma_from == 0.0f) {
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x[j] = vec_denoised[j];
|
||||
}
|
||||
} else {
|
||||
float t = t_fn(sigma_from);
|
||||
float t_next = t_fn(sigma_down);
|
||||
float h = t_next - t;
|
||||
|
||||
float a21 = c2 * phi1_fn(-h * c2);
|
||||
float phi1_val = phi1_fn(-h);
|
||||
float phi2_val = phi2_fn(-h);
|
||||
float b2 = phi2_val / c2;
|
||||
float b1 = phi1_val - b2;
|
||||
|
||||
float sigma_c2 = expf(-(t + h * c2));
|
||||
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
float* vec_x2 = (float*)x2->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
float eps1 = vec_denoised[j] - vec_x0[j];
|
||||
vec_x2[j] = vec_x0[j] + h * a21 * eps1;
|
||||
}
|
||||
|
||||
ggml_tensor* denoised2 = model(x2, sigma_c2, i + 1);
|
||||
if (denoised2 == nullptr) {
|
||||
return false;
|
||||
}
|
||||
float* vec_denoised2 = (float*)denoised2->data;
|
||||
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
float eps1 = vec_denoised[j] - vec_x0[j];
|
||||
float eps2 = vec_denoised2[j] - vec_x0[j];
|
||||
vec_x[j] = vec_x0[j] + h * (b1 * eps1 + b2 * eps2);
|
||||
}
|
||||
}
|
||||
|
||||
if (sigmas[i + 1] > 0 && sigma_up > 0.0f) {
|
||||
ggml_ext_im_set_randn_f32(noise, rng);
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_noise = (float*)noise->data;
|
||||
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x[j] = vec_x[j] + vec_noise[j] * sigma_up;
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_ERROR("Attempting to sample with nonexisting sample method %i", method);
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // __DENOISER_HPP__
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "qwen_image.hpp"
|
||||
#include "unet.hpp"
|
||||
#include "wan.hpp"
|
||||
#include "z_image.hpp"
|
||||
|
||||
struct DiffusionParams {
|
||||
struct ggml_tensor* x = nullptr;
|
||||
@@ -26,7 +27,7 @@ struct DiffusionParams {
|
||||
|
||||
struct DiffusionModel {
|
||||
virtual std::string get_desc() = 0;
|
||||
virtual void compute(int n_threads,
|
||||
virtual bool compute(int n_threads,
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = nullptr,
|
||||
struct ggml_context* output_ctx = nullptr) = 0;
|
||||
@@ -35,8 +36,10 @@ struct DiffusionModel {
|
||||
virtual void free_compute_buffer() = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) = 0;
|
||||
virtual size_t get_params_buffer_size() = 0;
|
||||
virtual int64_t get_adm_in_channels() = 0;
|
||||
virtual void set_flash_attn_enabled(bool enabled) = 0;
|
||||
virtual void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter){};
|
||||
virtual int64_t get_adm_in_channels() = 0;
|
||||
virtual void set_flash_attention_enabled(bool enabled) = 0;
|
||||
virtual void set_circular_axes(bool circular_x, bool circular_y) = 0;
|
||||
};
|
||||
|
||||
struct UNetModel : public DiffusionModel {
|
||||
@@ -73,15 +76,23 @@ struct UNetModel : public DiffusionModel {
|
||||
return unet.get_params_buffer_size();
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
unet.set_weight_adapter(adapter);
|
||||
}
|
||||
|
||||
int64_t get_adm_in_channels() override {
|
||||
return unet.unet.adm_in_channels;
|
||||
}
|
||||
|
||||
void set_flash_attn_enabled(bool enabled) {
|
||||
void set_flash_attention_enabled(bool enabled) {
|
||||
unet.set_flash_attention_enabled(enabled);
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||
unet.set_circular_axes(circular_x, circular_y);
|
||||
}
|
||||
|
||||
bool compute(int n_threads,
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = nullptr,
|
||||
struct ggml_context* output_ctx = nullptr) override {
|
||||
@@ -130,15 +141,23 @@ struct MMDiTModel : public DiffusionModel {
|
||||
return mmdit.get_params_buffer_size();
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
mmdit.set_weight_adapter(adapter);
|
||||
}
|
||||
|
||||
int64_t get_adm_in_channels() override {
|
||||
return 768 + 1280;
|
||||
}
|
||||
|
||||
void set_flash_attn_enabled(bool enabled) {
|
||||
void set_flash_attention_enabled(bool enabled) {
|
||||
mmdit.set_flash_attention_enabled(enabled);
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||
mmdit.set_circular_axes(circular_x, circular_y);
|
||||
}
|
||||
|
||||
bool compute(int n_threads,
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = nullptr,
|
||||
struct ggml_context* output_ctx = nullptr) override {
|
||||
@@ -188,15 +207,23 @@ struct FluxModel : public DiffusionModel {
|
||||
return flux.get_params_buffer_size();
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
flux.set_weight_adapter(adapter);
|
||||
}
|
||||
|
||||
int64_t get_adm_in_channels() override {
|
||||
return 768;
|
||||
}
|
||||
|
||||
void set_flash_attn_enabled(bool enabled) {
|
||||
void set_flash_attention_enabled(bool enabled) {
|
||||
flux.set_flash_attention_enabled(enabled);
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||
flux.set_circular_axes(circular_x, circular_y);
|
||||
}
|
||||
|
||||
bool compute(int n_threads,
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = nullptr,
|
||||
struct ggml_context* output_ctx = nullptr) override {
|
||||
@@ -251,15 +278,23 @@ struct WanModel : public DiffusionModel {
|
||||
return wan.get_params_buffer_size();
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
wan.set_weight_adapter(adapter);
|
||||
}
|
||||
|
||||
int64_t get_adm_in_channels() override {
|
||||
return 768;
|
||||
}
|
||||
|
||||
void set_flash_attn_enabled(bool enabled) {
|
||||
void set_flash_attention_enabled(bool enabled) {
|
||||
wan.set_flash_attention_enabled(enabled);
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||
wan.set_circular_axes(circular_x, circular_y);
|
||||
}
|
||||
|
||||
bool compute(int n_threads,
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = nullptr,
|
||||
struct ggml_context* output_ctx = nullptr) override {
|
||||
@@ -285,8 +320,9 @@ struct QwenImageModel : public DiffusionModel {
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "model.diffusion_model",
|
||||
SDVersion version = VERSION_QWEN_IMAGE)
|
||||
: prefix(prefix), qwen_image(backend, offload_params_to_cpu, tensor_storage_map, prefix, version) {
|
||||
SDVersion version = VERSION_QWEN_IMAGE,
|
||||
bool zero_cond_t = false)
|
||||
: prefix(prefix), qwen_image(backend, offload_params_to_cpu, tensor_storage_map, prefix, version, zero_cond_t) {
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
@@ -313,15 +349,23 @@ struct QwenImageModel : public DiffusionModel {
|
||||
return qwen_image.get_params_buffer_size();
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
qwen_image.set_weight_adapter(adapter);
|
||||
}
|
||||
|
||||
int64_t get_adm_in_channels() override {
|
||||
return 768;
|
||||
}
|
||||
|
||||
void set_flash_attn_enabled(bool enabled) {
|
||||
void set_flash_attention_enabled(bool enabled) {
|
||||
qwen_image.set_flash_attention_enabled(enabled);
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||
qwen_image.set_circular_axes(circular_x, circular_y);
|
||||
}
|
||||
|
||||
bool compute(int n_threads,
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = nullptr,
|
||||
struct ggml_context* output_ctx = nullptr) override {
|
||||
@@ -336,4 +380,71 @@ struct QwenImageModel : public DiffusionModel {
|
||||
}
|
||||
};
|
||||
|
||||
struct ZImageModel : public DiffusionModel {
|
||||
std::string prefix;
|
||||
ZImage::ZImageRunner z_image;
|
||||
|
||||
ZImageModel(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "model.diffusion_model",
|
||||
SDVersion version = VERSION_Z_IMAGE)
|
||||
: prefix(prefix), z_image(backend, offload_params_to_cpu, tensor_storage_map, prefix, version) {
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
return z_image.get_desc();
|
||||
}
|
||||
|
||||
void alloc_params_buffer() override {
|
||||
z_image.alloc_params_buffer();
|
||||
}
|
||||
|
||||
void free_params_buffer() override {
|
||||
z_image.free_params_buffer();
|
||||
}
|
||||
|
||||
void free_compute_buffer() override {
|
||||
z_image.free_compute_buffer();
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) override {
|
||||
z_image.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() override {
|
||||
return z_image.get_params_buffer_size();
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
z_image.set_weight_adapter(adapter);
|
||||
}
|
||||
|
||||
int64_t get_adm_in_channels() override {
|
||||
return 768;
|
||||
}
|
||||
|
||||
void set_flash_attention_enabled(bool enabled) {
|
||||
z_image.set_flash_attention_enabled(enabled);
|
||||
}
|
||||
|
||||
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||
z_image.set_circular_axes(circular_x, circular_y);
|
||||
}
|
||||
|
||||
bool compute(int n_threads,
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = nullptr,
|
||||
struct ggml_context* output_ctx = nullptr) override {
|
||||
return z_image.compute(n_threads,
|
||||
diffusion_params.x,
|
||||
diffusion_params.timesteps,
|
||||
diffusion_params.context,
|
||||
diffusion_params.ref_latents,
|
||||
true, // increase_ref_index
|
||||
output,
|
||||
output_ctx);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
## Caching
|
||||
|
||||
Caching methods accelerate diffusion inference by reusing intermediate computations when changes between steps are small.
|
||||
|
||||
### Cache Modes
|
||||
|
||||
| Mode | Target | Description |
|
||||
|------|--------|-------------|
|
||||
| `ucache` | UNET models | Condition-level caching with error tracking |
|
||||
| `easycache` | DiT models | Condition-level cache |
|
||||
| `dbcache` | DiT models | Block-level L1 residual threshold |
|
||||
| `taylorseer` | DiT models | Taylor series approximation |
|
||||
| `cache-dit` | DiT models | Combined DBCache + TaylorSeer |
|
||||
|
||||
### UCache (UNET Models)
|
||||
|
||||
UCache caches the residual difference (output - input) and reuses it when input changes are below threshold.
|
||||
|
||||
```bash
|
||||
sd-cli -m model.safetensors -p "a cat" --cache-mode ucache --cache-option "threshold=1.5"
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-----------|-------------|---------|
|
||||
| `threshold` | Error threshold for reuse decision | 1.0 |
|
||||
| `start` | Start caching at this percent of steps | 0.15 |
|
||||
| `end` | Stop caching at this percent of steps | 0.95 |
|
||||
| `decay` | Error decay rate (0-1) | 1.0 |
|
||||
| `relative` | Scale threshold by output norm (0/1) | 1 |
|
||||
| `reset` | Reset error after computing (0/1) | 1 |
|
||||
|
||||
#### Reset Parameter
|
||||
|
||||
The `reset` parameter controls error accumulation behavior:
|
||||
|
||||
- `reset=1` (default): Resets accumulated error after each computed step. More aggressive caching, works well with most samplers.
|
||||
- `reset=0`: Keeps error accumulated. More conservative, recommended for `euler_a` sampler.
|
||||
|
||||
### EasyCache (DiT Models)
|
||||
|
||||
Condition-level caching for DiT models. Caches and reuses outputs when input changes are below threshold.
|
||||
|
||||
```bash
|
||||
--cache-mode easycache --cache-option "threshold=0.3"
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-----------|-------------|---------|
|
||||
| `threshold` | Input change threshold for reuse | 0.2 |
|
||||
| `start` | Start caching at this percent of steps | 0.15 |
|
||||
| `end` | Stop caching at this percent of steps | 0.95 |
|
||||
|
||||
### Cache-DIT (DiT Models)
|
||||
|
||||
For DiT models like FLUX and QWEN, use block-level caching modes.
|
||||
|
||||
#### DBCache
|
||||
|
||||
Caches blocks based on L1 residual difference threshold:
|
||||
|
||||
```bash
|
||||
--cache-mode dbcache --cache-option "threshold=0.25,warmup=4"
|
||||
```
|
||||
|
||||
#### TaylorSeer
|
||||
|
||||
Uses Taylor series approximation to predict block outputs:
|
||||
|
||||
```bash
|
||||
--cache-mode taylorseer
|
||||
```
|
||||
|
||||
#### Cache-DIT (Combined)
|
||||
|
||||
Combines DBCache and TaylorSeer:
|
||||
|
||||
```bash
|
||||
--cache-mode cache-dit --cache-preset fast
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-----------|-------------|---------|
|
||||
| `Fn` | Front blocks to always compute | 8 |
|
||||
| `Bn` | Back blocks to always compute | 0 |
|
||||
| `threshold` | L1 residual difference threshold | 0.08 |
|
||||
| `warmup` | Steps before caching starts | 8 |
|
||||
|
||||
#### Presets
|
||||
|
||||
Available presets: `slow`, `medium`, `fast`, `ultra` (or `s`, `m`, `f`, `u`).
|
||||
|
||||
```bash
|
||||
--cache-mode cache-dit --cache-preset fast
|
||||
```
|
||||
|
||||
#### SCM Options
|
||||
|
||||
Steps Computation Mask controls which steps can be cached:
|
||||
|
||||
```bash
|
||||
--scm-mask "1,1,1,1,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,1"
|
||||
```
|
||||
|
||||
Mask values: `1` = compute, `0` = can cache.
|
||||
|
||||
| Policy | Description |
|
||||
|--------|-------------|
|
||||
| `dynamic` | Check threshold before caching |
|
||||
| `static` | Always cache on cacheable steps |
|
||||
|
||||
```bash
|
||||
--scm-policy dynamic
|
||||
```
|
||||
|
||||
### Performance Tips
|
||||
|
||||
- Start with default thresholds and adjust based on output quality
|
||||
- Lower threshold = better quality, less speedup
|
||||
- Higher threshold = more speedup, potential quality loss
|
||||
- More steps generally means more caching opportunities
|
||||
@@ -15,7 +15,7 @@ You can run Chroma using stable-diffusion.cpp with a GPU that has 6GB or even 4G
|
||||
You can download the preconverted gguf weights from [silveroxides/Chroma-GGUF](https://huggingface.co/silveroxides/Chroma-GGUF), this way you don't have to do the conversion yourself.
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M convert -m ..\..\ComfyUI\models\unet\chroma-unlocked-v40.safetensors -o ..\models\chroma-unlocked-v40-q8_0.gguf -v --type q8_0
|
||||
.\bin\Release\sd-cli.exe -M convert -m ..\..\ComfyUI\models\unet\chroma-unlocked-v40.safetensors -o ..\models\chroma-unlocked-v40-q8_0.gguf -v --type q8_0
|
||||
```
|
||||
|
||||
## Run
|
||||
@@ -24,7 +24,7 @@ You can download the preconverted gguf weights from [silveroxides/Chroma-GGUF](h
|
||||
For example:
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\chroma-unlocked-v40-q8_0.gguf --vae ..\models\ae.sft --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'chroma.cpp'" --cfg-scale 4.0 --sampling-method euler -v --chroma-disable-dit-mask --clip-on-cpu
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\models\chroma-unlocked-v40-q8_0.gguf --vae ..\models\ae.sft --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'chroma.cpp'" --cfg-scale 4.0 --sampling-method euler -v --chroma-disable-dit-mask --clip-on-cpu
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
## Examples
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\Chroma1-Radiance-v0.4-Q8_0.gguf --t5xxl ..\..\ComfyUI\models\clip\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'chroma radiance cpp'" --cfg-scale 4.0 --sampling-method euler -v
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\Chroma1-Radiance-v0.4-Q8_0.gguf --t5xxl ..\..\ComfyUI\models\clip\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'chroma radiance cpp'" --cfg-scale 4.0 --sampling-method euler -v
|
||||
```
|
||||
|
||||
<img alt="Chroma1-Radiance" src="../assets/flux/chroma1-radiance.png" />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Running distilled models: SSD1B and SDx.x with tiny U-Nets
|
||||
# Running distilled models: SSD1B, Vega and SDx.x with tiny U-Nets
|
||||
|
||||
## Preface
|
||||
|
||||
These models feature a reduced U-Net architecture. Unlike standard SDXL models, the SSD-1B U-Net contains only one middle block and fewer attention layers in its up- and down-blocks, resulting in significantly smaller file sizes. Using these models can reduce inference time by more than 33%. For more details, refer to Segmind's paper: https://arxiv.org/abs/2401.02677v1.
|
||||
These models feature a reduced U-Net architecture. Unlike standard SDXL models, the SSD-1B and Vega U-Net contains only one middle block and fewer attention layers in its up- and down-blocks, resulting in significantly smaller file sizes. Using these models can reduce inference time by more than 33%. For more details, refer to Segmind's paper: https://arxiv.org/abs/2401.02677v1.
|
||||
Similarly, SD1.x- and SD2.x-style models with a tiny U-Net consist of only 6 U-Net blocks, leading to very small files and time savings of up to 50%. For more information, see the paper: https://arxiv.org/pdf/2305.15798.pdf.
|
||||
|
||||
## SSD1B
|
||||
@@ -17,7 +17,17 @@ Useful LoRAs are also available:
|
||||
* https://huggingface.co/seungminh/lora-swarovski-SSD-1B/resolve/main/pytorch_lora_weights.safetensors
|
||||
* https://huggingface.co/kylielee505/mylcmlorassd/resolve/main/pytorch_lora_weights.safetensors
|
||||
|
||||
These files can be used out-of-the-box, unlike the models described in the next section.
|
||||
## Vega
|
||||
|
||||
Segmind's Vega model is available online here:
|
||||
|
||||
* https://huggingface.co/segmind/Segmind-Vega/resolve/main/segmind-vega.safetensors
|
||||
|
||||
VegaRT is an example for an LCM-LoRA:
|
||||
|
||||
* https://huggingface.co/segmind/Segmind-VegaRT/resolve/main/pytorch_lora_weights.safetensors
|
||||
|
||||
Both files can be used out-of-the-box, unlike the models described in next sections.
|
||||
|
||||
|
||||
## SD1.x, SD2.x with tiny U-Nets
|
||||
@@ -83,7 +93,7 @@ python convert_diffusers_to_original_stable_diffusion.py \
|
||||
The file segmind_tiny-sd.ckpt will be generated and is now ready for use with sd.cpp. You can follow a similar process for the other models mentioned above.
|
||||
|
||||
|
||||
### Another available .ckpt file:
|
||||
##### Another available .ckpt file:
|
||||
|
||||
* https://huggingface.co/ClashSAN/small-sd/resolve/main/tinySDdistilled.ckpt
|
||||
|
||||
@@ -97,3 +107,31 @@ for key, value in ckpt['state_dict'].items():
|
||||
ckpt['state_dict'][key] = value.contiguous()
|
||||
torch.save(ckpt, "tinySDdistilled_fixed.ckpt")
|
||||
```
|
||||
|
||||
|
||||
### SDXS-512
|
||||
|
||||
Another very tiny and **incredibly fast** model is SDXS by IDKiro et al. The authors refer to it as *"Real-Time One-Step Latent Diffusion Models with Image Conditions"*. For details read the paper: https://arxiv.org/pdf/2403.16627 . Once again the authors removed some more blocks of U-Net part and unlike other SD1 models they use an adjusted _AutoEncoderTiny_ instead of default _AutoEncoderKL_ for the VAE part.
|
||||
|
||||
##### 1. Download the diffusers model from Hugging Face using Python:
|
||||
|
||||
```python
|
||||
from diffusers import StableDiffusionPipeline
|
||||
pipe = StableDiffusionPipeline.from_pretrained("IDKiro/sdxs-512-dreamshaper")
|
||||
pipe.save_pretrained(save_directory="sdxs")
|
||||
```
|
||||
##### 2. Create a safetensors file
|
||||
|
||||
```bash
|
||||
python convert_diffusers_to_original_stable_diffusion.py \
|
||||
--model_path sdxs --checkpoint_path sdxs.safetensors --half --use_safetensors
|
||||
```
|
||||
|
||||
##### 3. Run the model as follows:
|
||||
|
||||
```bash
|
||||
~/stable-diffusion.cpp/build/bin/sd-cli -m sdxs.safetensors -p "portrait of a lovely cat" \
|
||||
--cfg-scale 1 --steps 1
|
||||
```
|
||||
|
||||
Both options: ``` --cfg-scale 1 ``` and ``` --steps 1 ``` are mandatory here.
|
||||
|
||||
@@ -1,15 +1,39 @@
|
||||
## Docker
|
||||
# Docker
|
||||
|
||||
### Building using Docker
|
||||
## Run CLI
|
||||
|
||||
```shell
|
||||
docker run --rm -v /path/to/models:/models -v /path/to/output/:/output ghcr.io/leejet/stable-diffusion.cpp:master [args...]
|
||||
# For example
|
||||
# docker run --rm -v ./models:/models -v ./build:/output ghcr.io/leejet/stable-diffusion.cpp:master -m /models/sd-v1-4.ckpt -p "a lovely cat" -v -o /output/output.png
|
||||
```
|
||||
|
||||
## Run server
|
||||
|
||||
```shell
|
||||
docker run --rm --init -v /path/to/models:/models -v /path/to/output/:/output -p "1234:1234" --entrypoint "/sd-server" ghcr.io/leejet/stable-diffusion.cpp:master [args...]
|
||||
# For example
|
||||
# docker run --rm --init -v ./models:/models -v ./build:/output -p "1234:1234" --entrypoint "/sd-server" ghcr.io/leejet/stable-diffusion.cpp:master -m /models/sd-v1-4.ckpt -p "a lovely cat" -v -o /output/output.png
|
||||
```
|
||||
|
||||
## Building using Docker
|
||||
|
||||
```shell
|
||||
docker build -t sd .
|
||||
```
|
||||
|
||||
### Run
|
||||
## Building variants using Docker
|
||||
|
||||
Vulkan:
|
||||
|
||||
```shell
|
||||
docker run -v /path/to/models:/models -v /path/to/output/:/output sd [args...]
|
||||
docker build -f Dockerfile.vulkan -t sd .
|
||||
```
|
||||
|
||||
## Run locally built image's CLI
|
||||
|
||||
```shell
|
||||
docker run --rm -v /path/to/models:/models -v /path/to/output/:/output sd [args...]
|
||||
# For example
|
||||
# docker run -v ./models:/models -v ./build:/output sd -m /models/sd-v1-4.ckpt -p "a lovely cat" -v -o /output/output.png
|
||||
```
|
||||
# docker run --rm -v ./models:/models -v ./build:/output sd -m /models/sd-v1-4.ckpt -p "a lovely cat" -v -o /output/output.png
|
||||
```
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
## Using ESRGAN to upscale results
|
||||
|
||||
You can use ESRGAN to upscale the generated images. At the moment, only the [RealESRGAN_x4plus_anime_6B.pth](https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth) model is supported. Support for more models of this architecture will be added soon.
|
||||
You can use ESRGAN—such as the model [RealESRGAN_x4plus_anime_6B.pth](https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth)—to upscale the generated images and improve their overall resolution and clarity.
|
||||
|
||||
- Specify the model path using the `--upscale-model PATH` parameter. example:
|
||||
|
||||
```bash
|
||||
sd -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat" --upscale-model ../models/RealESRGAN_x4plus_anime_6B.pth
|
||||
sd-cli -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat" --upscale-model ../models/RealESRGAN_x4plus_anime_6B.pth
|
||||
```
|
||||
|
||||
@@ -15,9 +15,9 @@ You can run Flux using stable-diffusion.cpp with a GPU that has 6GB or even 4GB
|
||||
|
||||
You can download the preconverted gguf weights from [FLUX.1-dev-gguf](https://huggingface.co/leejet/FLUX.1-dev-gguf) or [FLUX.1-schnell](https://huggingface.co/leejet/FLUX.1-schnell-gguf), this way you don't have to do the conversion yourself.
|
||||
|
||||
Using fp16 will lead to overflow, but ggml's support for bf16 is not yet fully developed. Therefore, we need to convert flux to gguf format here, which also saves VRAM. For example:
|
||||
For example:
|
||||
```
|
||||
.\bin\Release\sd.exe -M convert -m ..\..\ComfyUI\models\unet\flux1-dev.sft -o ..\models\flux1-dev-q8_0.gguf -v --type q8_0
|
||||
.\bin\Release\sd-cli.exe -M convert -m ..\..\ComfyUI\models\unet\flux1-dev.sft -o ..\models\flux1-dev-q8_0.gguf -v --type q8_0
|
||||
```
|
||||
|
||||
## Run
|
||||
@@ -28,7 +28,7 @@ Using fp16 will lead to overflow, but ggml's support for bf16 is not yet fully d
|
||||
For example:
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\flux1-dev-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v --clip-on-cpu
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\models\flux1-dev-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v --clip-on-cpu
|
||||
```
|
||||
|
||||
Using formats of different precisions will yield results of varying quality.
|
||||
@@ -44,7 +44,7 @@ Using formats of different precisions will yield results of varying quality.
|
||||
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\flux1-schnell-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v --steps 4 --clip-on-cpu
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\models\flux1-schnell-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v --steps 4 --clip-on-cpu
|
||||
```
|
||||
|
||||
| q8_0 |
|
||||
@@ -60,7 +60,7 @@ Since many flux LoRA training libraries have used various LoRA naming formats, i
|
||||
- LoRA model from https://huggingface.co/XLabs-AI/flux-lora-collection/tree/main (using comfy converted version!!!)
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\flux1-dev-q8_0.gguf --vae ...\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'<lora:realism_lora_comfy_converted:1>" --cfg-scale 1.0 --sampling-method euler -v --lora-model-dir ../models --clip-on-cpu
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\models\flux1-dev-q8_0.gguf --vae ...\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'<lora:realism_lora_comfy_converted:1>" --cfg-scale 1.0 --sampling-method euler -v --lora-model-dir ../models --clip-on-cpu
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
# How to Use
|
||||
|
||||
## Flux.2-dev
|
||||
|
||||
### Download weights
|
||||
|
||||
- Download FLUX.2-dev
|
||||
- gguf: https://huggingface.co/city96/FLUX.2-dev-gguf/tree/main
|
||||
- Download vae
|
||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.2-dev/tree/main
|
||||
- Download Mistral-Small-3.2-24B-Instruct-2506-GGUF
|
||||
- gguf: https://huggingface.co/unsloth/Mistral-Small-3.2-24B-Instruct-2506-GGUF/tree/main
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\flux2-dev-Q4_K_S.gguf --vae ..\..\ComfyUI\models\vae\flux2_ae.safetensors --llm ..\..\ComfyUI\models\text_encoders\Mistral-Small-3.2-24B-Instruct-2506-Q4_K_M.gguf -r .\kontext_input.png -p "change 'flux.cpp' to 'flux2-dev.cpp'" --cfg-scale 1.0 --sampling-method euler -v --diffusion-fa --offload-to-cpu
|
||||
```
|
||||
|
||||
<img alt="flux2 example" src="../assets/flux2/example.png" />
|
||||
|
||||
## Flux.2 klein 4B / Flux.2 klein base 4B
|
||||
|
||||
### Download weights
|
||||
|
||||
- Download FLUX.2-klein-4B
|
||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.2-klein-4B
|
||||
- gguf: https://huggingface.co/leejet/FLUX.2-klein-4B-GGUF/tree/main
|
||||
- Download FLUX.2-klein-base-4B
|
||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.2-klein-base-4B
|
||||
- gguf: https://huggingface.co/leejet/FLUX.2-klein-base-4B-GGUF/tree/main
|
||||
- Download vae
|
||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.2-dev/tree/main
|
||||
- Download Qwen3 4b
|
||||
- safetensors: https://huggingface.co/Comfy-Org/flux2-klein-4B/tree/main/split_files/text_encoders
|
||||
- gguf: https://huggingface.co/unsloth/Qwen3-4B-GGUF/tree/main
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\flux-2-klein-4b.safetensors --vae ..\..\ComfyUI\models\vae\flux2_ae.safetensors --llm ..\..\ComfyUI\models\text_encoders\qwen_3_4b.safetensors -p "a lovely cat" --cfg-scale 1.0 --steps 4 -v --offload-to-cpu --diffusion-fa
|
||||
```
|
||||
|
||||
<img alt="flux2-klein-4b" src="../assets/flux2/flux2-klein-4b.png" />
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\flux-2-klein-4b.safetensors --vae ..\..\ComfyUI\models\vae\flux2_ae.safetensors --llm ..\..\ComfyUI\models\text_encoders\qwen_3_4b.safetensors -r .\kontext_input.png -p "change 'flux.cpp' to 'klein.cpp'" --cfg-scale 1.0 --sampling-method euler -v --diffusion-fa --offload-to-cpu --steps 4
|
||||
```
|
||||
|
||||
<img alt="flux2-klein-4b-edit" src="../assets/flux2/flux2-klein-4b-edit.png" />
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\flux-2-klein-base-4b.safetensors --vae ..\..\ComfyUI\models\vae\flux2_ae.safetensors --llm ..\..\ComfyUI\models\text_encoders\qwen_3_4b.safetensors -p "a lovely cat" --cfg-scale 4.0 --steps 20 -v --offload-to-cpu --diffusion-fa
|
||||
```
|
||||
|
||||
<img alt="flux2-klein-base-4b" src="../assets/flux2/flux2-klein-base-4b.png" />
|
||||
|
||||
## Flux.2 klein 9B / Flux.2 klein base 9B
|
||||
|
||||
### Download weights
|
||||
|
||||
- Download FLUX.2-klein-9B
|
||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.2-klein-9B
|
||||
- gguf: https://huggingface.co/leejet/FLUX.2-klein-9B-GGUF/tree/main
|
||||
- Download FLUX.2-klein-base-9B
|
||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.2-klein-base-9B
|
||||
- gguf: https://huggingface.co/leejet/FLUX.2-klein-base-9B-GGUF/tree/main
|
||||
- Download vae
|
||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.2-dev/tree/main
|
||||
- Download Qwen3 8B
|
||||
- safetensors: https://huggingface.co/Comfy-Org/flux2-klein-9B/tree/main/split_files/text_encoders
|
||||
- gguf: https://huggingface.co/unsloth/Qwen3-8B-GGUF/tree/main
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\flux-2-klein-9b.safetensors --vae ..\..\ComfyUI\models\vae\flux2_ae.safetensors --llm ..\..\ComfyUI\models\text_encoders\qwen_3_8b.safetensors -p "a lovely cat" --cfg-scale 1.0 --steps 4 -v --offload-to-cpu --diffusion-fa
|
||||
```
|
||||
|
||||
<img alt="flux2-klein-9b" src="../assets/flux2/flux2-klein-9b.png" />
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\flux-2-klein-9b.safetensors --vae ..\..\ComfyUI\models\vae\flux2_ae.safetensors --llm ..\..\ComfyUI\models\text_encoders\qwen_3_8b.safetensors -r .\kontext_input.png -p "change 'flux.cpp' to 'klein.cpp'" --cfg-scale 1.0 --sampling-method euler -v --diffusion-fa --offload-to-cpu --steps 4
|
||||
```
|
||||
|
||||
<img alt="flux2-klein-9b-edit" src="../assets/flux2/flux2-klein-9b-edit.png" />
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\flux-2-klein-base-9b.safetensors --vae ..\..\ComfyUI\models\vae\flux2_ae.safetensors --llm ..\..\ComfyUI\models\text_encoders\qwen_3_8b.safetensors -p "a lovely cat" --cfg-scale 4.0 --steps 20 -v --offload-to-cpu --diffusion-fa
|
||||
```
|
||||
|
||||
<img alt="flux2-klein-base-9b" src="../assets/flux2/flux2-klein-base-9b.png" />
|
||||
@@ -82,4 +82,4 @@ cmake .. -G "Ninja" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DSD_H
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
If everything went OK, `build\bin\sd.exe` file should appear.
|
||||
If everything went OK, `build\bin\sd-cli.exe` file should appear.
|
||||
|
||||
@@ -16,7 +16,7 @@ You can run Kontext using stable-diffusion.cpp with a GPU that has 6GB or even 4
|
||||
You can download the preconverted gguf weights from [FLUX.1-Kontext-dev-GGUF](https://huggingface.co/QuantStack/FLUX.1-Kontext-dev-GGUF), this way you don't have to do the conversion yourself.
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M convert -m ..\..\ComfyUI\models\unet\flux1-kontext-dev.safetensors -o ..\models\flux1-kontext-dev-q8_0.gguf -v --type q8_0
|
||||
.\bin\Release\sd-cli.exe -M convert -m ..\..\ComfyUI\models\unet\flux1-kontext-dev.safetensors -o ..\models\flux1-kontext-dev-q8_0.gguf -v --type q8_0
|
||||
```
|
||||
|
||||
## Run
|
||||
@@ -27,7 +27,7 @@ You can download the preconverted gguf weights from [FLUX.1-Kontext-dev-GGUF](ht
|
||||
For example:
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -r .\flux1-dev-q8_0.png --diffusion-model ..\models\flux1-kontext-dev-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "change 'flux.cpp' to 'kontext.cpp'" --cfg-scale 1.0 --sampling-method euler -v --clip-on-cpu
|
||||
.\bin\Release\sd-cli.exe -r .\flux1-dev-q8_0.png --diffusion-model ..\models\flux1-kontext-dev-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "change 'flux.cpp' to 'kontext.cpp'" --cfg-scale 1.0 --sampling-method euler -v --clip-on-cpu
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
Here's a simple example:
|
||||
|
||||
```
|
||||
./bin/sd -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat<lora:lcm-lora-sdv1-5:1>" --steps 4 --lora-model-dir ../models -v --cfg-scale 1
|
||||
./bin/sd-cli -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat<lora:lcm-lora-sdv1-5:1>" --steps 4 --lora-model-dir ../models -v --cfg-scale 1
|
||||
```
|
||||
|
||||
| without LCM-LoRA (--cfg-scale 7) | with LCM-LoRA (--cfg-scale 1) |
|
||||
|
||||
@@ -7,43 +7,20 @@
|
||||
Here's a simple example:
|
||||
|
||||
```
|
||||
./bin/sd -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat<lora:marblesh:1>" --lora-model-dir ../models
|
||||
./bin/sd-cli -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat<lora:marblesh:1>" --lora-model-dir ../models
|
||||
```
|
||||
|
||||
`../models/marblesh.safetensors` or `../models/marblesh.ckpt` will be applied to the model
|
||||
|
||||
# Support matrix
|
||||
# Lora Apply Mode
|
||||
|
||||
> ℹ️ CUDA `get_rows` support is defined here:
|
||||
> [ggml-org/ggml/src/ggml-cuda/getrows.cu#L156](https://github.com/ggml-org/ggml/blob/7dee1d6a1e7611f238d09be96738388da97c88ed/src/ggml-cuda/getrows.cu#L156)
|
||||
> Currently only the basic types + Q4/Q5/Q8 are implemented. K-quants are **not** supported.
|
||||
There are two ways to apply LoRA: **immediately** and **at_runtime**. You can specify it using the `--lora-apply-mode` parameter.
|
||||
|
||||
NOTE: The other backends may have different support.
|
||||
By default, the mode is selected automatically:
|
||||
|
||||
* If the model weights contain any quantized parameters, the **at_runtime** mode is used;
|
||||
* Otherwise, the **immediately** mode is used.
|
||||
|
||||
The **immediately** mode may have precision and compatibility issues with quantized parameters, but it usually offers faster inference speed and, in some cases, lower memory usage.
|
||||
In contrast, the **at_runtime** mode provides better compatibility and higher precision, but inference may be slower and memory usage may be higher in some cases.
|
||||
|
||||
| Quant / Type | CUDA | Vulkan |
|
||||
|--------------|------|--------|
|
||||
| F32 | ✔️ | ✔️ |
|
||||
| F16 | ✔️ | ✔️ |
|
||||
| BF16 | ✔️ | ✔️ |
|
||||
| I32 | ✔️ | ❌ |
|
||||
| Q4_0 | ✔️ | ✔️ |
|
||||
| Q4_1 | ✔️ | ✔️ |
|
||||
| Q5_0 | ✔️ | ✔️ |
|
||||
| Q5_1 | ✔️ | ✔️ |
|
||||
| Q8_0 | ✔️ | ✔️ |
|
||||
| Q2_K | ❌ | ❌ |
|
||||
| Q3_K | ❌ | ❌ |
|
||||
| Q4_K | ❌ | ❌ |
|
||||
| Q5_K | ❌ | ❌ |
|
||||
| Q6_K | ❌ | ❌ |
|
||||
| Q8_K | ❌ | ❌ |
|
||||
| IQ1_S | ❌ | ✔️ |
|
||||
| IQ1_M | ❌ | ✔️ |
|
||||
| IQ2_XXS | ❌ | ✔️ |
|
||||
| IQ2_XS | ❌ | ✔️ |
|
||||
| IQ2_S | ❌ | ✔️ |
|
||||
| IQ3_XXS | ❌ | ✔️ |
|
||||
| IQ3_S | ❌ | ✔️ |
|
||||
| IQ4_XS | ❌ | ✔️ |
|
||||
| IQ4_NL | ❌ | ✔️ |
|
||||
| MXFP4 | ❌ | ✔️ |
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# How to Use
|
||||
|
||||
## Download weights
|
||||
|
||||
- Download Ovis-Image-7B
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Ovis-Image/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/leejet/Ovis-Image-7B-GGUF
|
||||
- Download vae
|
||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.1-schnell/tree/main
|
||||
- Download Ovis 2.5
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Ovis-Image/tree/main/split_files/text_encoders
|
||||
|
||||
## Examples
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ovis_image-Q4_0.gguf --vae ..\..\ComfyUI\models\vae\ae.sft --llm ..\..\ComfyUI\models\text_encoders\ovis_2.5.safetensors -p "a lovely cat" --cfg-scale 5.0 -v --offload-to-cpu --diffusion-fa
|
||||
```
|
||||
|
||||
<img alt="ovis image example" src="../assets/ovis_image/example.png" />
|
||||
@@ -27,7 +27,7 @@ If on low memory GPUs (<= 8GB), recommend running with ```--vae-on-cpu``` option
|
||||
Example:
|
||||
|
||||
```bash
|
||||
bin/sd -m ../models/sdxlUnstableDiffusers_v11.safetensors --vae ../models/sdxl_vae.safetensors --photo-maker ../models/photomaker-v1.safetensors --pm-id-images-dir ../assets/photomaker_examples/scarletthead_woman -p "a girl img, retro futurism, retro game art style but extremely beautiful, intricate details, masterpiece, best quality, space-themed, cosmic, celestial, stars, galaxies, nebulas, planets, science fiction, highly detailed" -n "realistic, photo-realistic, worst quality, greyscale, bad anatomy, bad hands, error, text" --cfg-scale 5.0 --sampling-method euler -H 1024 -W 1024 --pm-style-strength 10 --vae-on-cpu --steps 50
|
||||
bin/sd-cli -m ../models/sdxlUnstableDiffusers_v11.safetensors --vae ../models/sdxl_vae.safetensors --photo-maker ../models/photomaker-v1.safetensors --pm-id-images-dir ../assets/photomaker_examples/scarletthead_woman -p "a girl img, retro futurism, retro game art style but extremely beautiful, intricate details, masterpiece, best quality, space-themed, cosmic, celestial, stars, galaxies, nebulas, planets, science fiction, highly detailed" -n "realistic, photo-realistic, worst quality, greyscale, bad anatomy, bad hands, error, text" --cfg-scale 5.0 --sampling-method euler -H 1024 -W 1024 --pm-style-strength 10 --vae-on-cpu --steps 50
|
||||
```
|
||||
|
||||
## PhotoMaker Version 2
|
||||
|
||||
@@ -23,5 +23,5 @@ You can also convert weights in the formats `ckpt/safetensors/diffusers` to gguf
|
||||
For example:
|
||||
|
||||
```sh
|
||||
./bin/sd -M convert -m ../models/v1-5-pruned-emaonly.safetensors -o ../models/v1-5-pruned-emaonly.q8_0.gguf -v --type q8_0
|
||||
./bin/sd-cli -M convert -m ../models/v1-5-pruned-emaonly.safetensors -o ../models/v1-5-pruned-emaonly.q8_0.gguf -v --type q8_0
|
||||
```
|
||||
@@ -14,7 +14,7 @@
|
||||
## Examples
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\qwen-image-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --qwen2vl ..\..\ComfyUI\models\text_encoders\Qwen2.5-VL-7B-Instruct-Q8_0.gguf -p '一个穿着"QWEN"标志的T恤的中国美女正拿着黑色的马克笔面相镜头微笑。她身后的玻璃板上手写体写着 “一、Qwen-Image的技术路线: 探索视觉生成基础模型的极限,开创理解与生成一体化的未来。二、Qwen-Image的模型特色:1、复杂文字渲染。支持中英渲染、自动布局; 2、精准图像编辑。支持文字编辑、物体增减、风格变换。三、Qwen-Image的未来愿景:赋能专业内容创作、助力生成式AI发展。”' --cfg-scale 2.5 --sampling-method euler -v --offload-to-cpu -H 1024 -W 1024 --diffusion-fa --flow-shift 3
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\qwen-image-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --llm ..\..\ComfyUI\models\text_encoders\Qwen2.5-VL-7B-Instruct-Q8_0.gguf -p '一个穿着"QWEN"标志的T恤的中国美女正拿着黑色的马克笔面相镜头微笑。她身后的玻璃板上手写体写着 “一、Qwen-Image的技术路线: 探索视觉生成基础模型的极限,开创理解与生成一体化的未来。二、Qwen-Image的模型特色:1、复杂文字渲染。支持中英渲染、自动布局; 2、精准图像编辑。支持文字编辑、物体增减、风格变换。三、Qwen-Image的未来愿景:赋能专业内容创作、助力生成式AI发展。”' --cfg-scale 2.5 --sampling-method euler -v --offload-to-cpu -H 1024 -W 1024 --diffusion-fa --flow-shift 3
|
||||
```
|
||||
|
||||
<img alt="qwen example" src="../assets/qwen/example.png" />
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
- Qwen Image Edit 2509
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/QuantStack/Qwen-Image-Edit-2509-GGUF/tree/main
|
||||
- Qwen Image Edit 2511
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/unsloth/Qwen-Image-Edit-2511-GGUF/tree/main
|
||||
- Download vae
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/tree/main/split_files/vae
|
||||
- Download qwen_2.5_vl 7b
|
||||
@@ -20,7 +23,7 @@
|
||||
### Qwen Image Edit
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\Qwen_Image_Edit-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --qwen2vl ..\..\ComfyUI\models\text_encoders\qwen_2.5_vl_7b.safetensors --cfg-scale 2.5 --sampling-method euler -v --offload-to-cpu --diffusion-fa --flow-shift 3 -r ..\assets\flux\flux1-dev-q8_0.png -p "change 'flux.cpp' to 'edit.cpp'" --seed 1118877715456453
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\Qwen_Image_Edit-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --llm ..\..\ComfyUI\models\text_encoders\qwen_2.5_vl_7b.safetensors --cfg-scale 2.5 --sampling-method euler -v --offload-to-cpu --diffusion-fa --flow-shift 3 -r ..\assets\flux\flux1-dev-q8_0.png -p "change 'flux.cpp' to 'edit.cpp'" --seed 1118877715456453
|
||||
```
|
||||
|
||||
<img alt="qwen_image_edit" src="../assets/qwen/qwen_image_edit.png" />
|
||||
@@ -29,7 +32,17 @@
|
||||
### Qwen Image Edit 2509
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\Qwen-Image-Edit-2509-Q4_K_S.gguf --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --qwen2vl ..\..\ComfyUI\models\text_encoders\Qwen2.5-VL-7B-Instruct-Q8_0.gguf --qwen2vl_vision ..\..\ComfyUI\models\text_encoders\Qwen2.5-VL-7B-Instruct.mmproj-Q8_0.gguf --cfg-scale 2.5 --sampling-method euler -v --offload-to-cpu --diffusion-fa --flow-shift 3 -r ..\assets\flux\flux1-dev-q8_0.png -p "change 'flux.cpp' to 'Qwen Image Edit 2509'"
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\Qwen-Image-Edit-2509-Q4_K_S.gguf --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --llm ..\..\ComfyUI\models\text_encoders\Qwen2.5-VL-7B-Instruct-Q8_0.gguf --llm_vision ..\..\ComfyUI\models\text_encoders\Qwen2.5-VL-7B-Instruct.mmproj-Q8_0.gguf --cfg-scale 2.5 --sampling-method euler -v --offload-to-cpu --diffusion-fa --flow-shift 3 -r ..\assets\flux\flux1-dev-q8_0.png -p "change 'flux.cpp' to 'Qwen Image Edit 2509'"
|
||||
```
|
||||
|
||||
<img alt="qwen_image_edit_2509" src="../assets/qwen/qwen_image_edit_2509.png" />
|
||||
<img alt="qwen_image_edit_2509" src="../assets/qwen/qwen_image_edit_2509.png" />
|
||||
|
||||
### Qwen Image Edit 2511
|
||||
|
||||
To use the new Qwen Image Edit 2511 mode, the `--qwen-image-zero-cond-t` flag must be enabled; otherwise, image editing quality will degrade significantly.
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\qwen-image-edit-2511-Q4_K_M.gguf --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --llm ..\..\ComfyUI\models\text_encoders\qwen_2.5_vl_7b.safetensors --cfg-scale 2.5 --sampling-method euler -v --offload-to-cpu --diffusion-fa --flow-shift 3 -r ..\assets\flux\flux1-dev-q8_0.png -p "change 'flux.cpp' to 'edit.cpp'" --qwen-image-zero-cond-t
|
||||
```
|
||||
|
||||
<img alt="qwen_image_edit_2509" src="../assets/qwen/qwen_image_edit_2511.png" />
|
||||
@@ -9,12 +9,12 @@
|
||||
### txt2img example
|
||||
|
||||
```sh
|
||||
./bin/sd -m ../models/sd-v1-4.ckpt -p "a lovely cat"
|
||||
# ./bin/sd -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat"
|
||||
# ./bin/sd -m ../models/sd_xl_base_1.0.safetensors --vae ../models/sdxl_vae-fp16-fix.safetensors -H 1024 -W 1024 -p "a lovely cat" -v
|
||||
# ./bin/sd -m ../models/sd3_medium_incl_clips_t5xxlfp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable Diffusion CPP\"' --cfg-scale 4.5 --sampling-method euler -v --clip-on-cpu
|
||||
# ./bin/sd --diffusion-model ../models/flux1-dev-q3_k.gguf --vae ../models/ae.sft --clip_l ../models/clip_l.safetensors --t5xxl ../models/t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v --clip-on-cpu
|
||||
# ./bin/sd -m ..\models\sd3.5_large.safetensors --clip_l ..\models\clip_l.safetensors --clip_g ..\models\clip_g.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable diffusion 3.5 Large\"' --cfg-scale 4.5 --sampling-method euler -v --clip-on-cpu
|
||||
./bin/sd-cli -m ../models/sd-v1-4.ckpt -p "a lovely cat"
|
||||
# ./bin/sd-cli -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat"
|
||||
# ./bin/sd-cli -m ../models/sd_xl_base_1.0.safetensors --vae ../models/sdxl_vae-fp16-fix.safetensors -H 1024 -W 1024 -p "a lovely cat" -v
|
||||
# ./bin/sd-cli -m ../models/sd3_medium_incl_clips_t5xxlfp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable Diffusion CPP\"' --cfg-scale 4.5 --sampling-method euler -v --clip-on-cpu
|
||||
# ./bin/sd-cli --diffusion-model ../models/flux1-dev-q3_k.gguf --vae ../models/ae.sft --clip_l ../models/clip_l.safetensors --t5xxl ../models/t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v --clip-on-cpu
|
||||
# ./bin/sd-cli -m ..\models\sd3.5_large.safetensors --clip_l ..\models\clip_l.safetensors --clip_g ..\models\clip_g.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable diffusion 3.5 Large\"' --cfg-scale 4.5 --sampling-method euler -v --clip-on-cpu
|
||||
```
|
||||
|
||||
Using formats of different precisions will yield results of varying quality.
|
||||
@@ -29,7 +29,7 @@ Using formats of different precisions will yield results of varying quality.
|
||||
|
||||
|
||||
```
|
||||
./bin/sd -m ../models/sd-v1-4.ckpt -p "cat with blue eyes" -i ./output.png -o ./img2img_output.png --strength 0.4
|
||||
./bin/sd-cli -m ../models/sd-v1-4.ckpt -p "cat with blue eyes" -i ./output.png -o ./img2img_output.png --strength 0.4
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
For example:
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -m ..\models\sd3.5_large.safetensors --clip_l ..\models\clip_l.safetensors --clip_g ..\models\clip_g.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable diffusion 3.5 Large\"' --cfg-scale 4.5 --sampling-method euler -v --clip-on-cpu
|
||||
.\bin\Release\sd-cli.exe -m ..\models\sd3.5_large.safetensors --clip_l ..\models\clip_l.safetensors --clip_g ..\models\clip_g.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable diffusion 3.5 Large\"' --cfg-scale 4.5 --sampling-method euler -v --clip-on-cpu
|
||||
```
|
||||
|
||||

|
||||
@@ -13,5 +13,27 @@ curl -L -O https://huggingface.co/madebyollin/taesd/resolve/main/diffusion_pytor
|
||||
- Specify the model path using the `--taesd PATH` parameter. example:
|
||||
|
||||
```bash
|
||||
sd -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat" --taesd ../models/diffusion_pytorch_model.safetensors
|
||||
```
|
||||
sd-cli -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat" --taesd ../models/diffusion_pytorch_model.safetensors
|
||||
```
|
||||
|
||||
### Qwen-Image and wan (TAEHV)
|
||||
|
||||
sd.cpp also supports [TAEHV](https://github.com/madebyollin/taehv) (#937), which can be used for Qwen-Image and wan.
|
||||
|
||||
- For **Qwen-Image and wan2.1 and wan2.2-A14B**, download the wan2.1 tae [safetensors weights](https://github.com/madebyollin/taehv/blob/main/safetensors/taew2_1.safetensors)
|
||||
|
||||
Or curl
|
||||
|
||||
```bash
|
||||
curl -L -O https://github.com/madebyollin/taehv/raw/refs/heads/main/safetensors/taew2_1.safetensors
|
||||
```
|
||||
|
||||
- For **wan2.2-TI2V-5B**, use the wan2.2 tae [safetensors weights](https://github.com/madebyollin/taehv/blob/main/safetensors/taew2_2.safetensors)
|
||||
|
||||
Or curl
|
||||
|
||||
```bash
|
||||
curl -L -O https://github.com/madebyollin/taehv/raw/refs/heads/main/safetensors/taew2_2.safetensors
|
||||
```
|
||||
|
||||
Then simply replace the `--vae xxx.safetensors` with `--tae xxx.safetensors` in the commands. If it still out of VRAM, add `--vae-conv-direct` to your command though might be slower.
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/blob/main/split_files/vae/wan_2.1_vae.safetensors
|
||||
- wan_2.2_vae (for Wan2.2 TI2V 5B only)
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/blob/main/split_files/vae/wan2.2_vae.safetensors
|
||||
|
||||
> Wan models vae requires really much VRAM! If you do not have enough VRAM, please try tae instead, though the results may be poorer. For tae usage, please refer to [taesd](taesd.md)
|
||||
|
||||
- Download umt5_xxl
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/blob/main/split_files/text_encoders/umt5_xxl_fp16.safetensors
|
||||
- gguf: https://huggingface.co/city96/umt5-xxl-encoder-gguf/tree/main
|
||||
@@ -52,7 +55,7 @@
|
||||
### Wan2.1 T2V 1.3B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1_t2v_1.3B_fp16.safetensors --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --video-frames 33 --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1_t2v_1.3B_fp16.safetensors --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --video-frames 33 --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_1.3B_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -60,7 +63,7 @@
|
||||
### Wan2.1 T2V 14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-t2v-14b-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --video-frames 33 --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-t2v-14b-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --video-frames 33 --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -70,7 +73,7 @@
|
||||
### Wan2.1 I2V 14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-i2v-14b-480p-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf --clip_vision ..\..\ComfyUI\models\clip_vision\clip_vision_h.safetensors -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --video-frames 33 --offload-to-cpu -i ..\assets\cat_with_sd_cpp_42.png --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-i2v-14b-480p-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf --clip_vision ..\..\ComfyUI\models\clip_vision\clip_vision_h.safetensors -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --video-frames 33 --offload-to-cpu -i ..\assets\cat_with_sd_cpp_42.png --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_i2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -78,7 +81,7 @@
|
||||
### Wan2.2 T2V A14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --video-frames 33 --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --video-frames 33 --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_14B_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -86,7 +89,7 @@
|
||||
### Wan2.2 I2V A14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --video-frames 33 --offload-to-cpu -i ..\assets\cat_with_sd_cpp_42.png --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --video-frames 33 --offload-to-cpu -i ..\assets\cat_with_sd_cpp_42.png --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_14B_i2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -94,7 +97,7 @@
|
||||
### Wan2.2 T2V A14B T2I
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --flow-shift 3.0
|
||||
```
|
||||
|
||||
<img width="832" height="480" alt="Wan2 2_14B_t2i" src="../assets/wan/Wan2.2_14B_t2i.png" />
|
||||
@@ -102,7 +105,7 @@
|
||||
### Wan2.2 T2V 14B with Lora
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat<lora:wan2.2_t2v_lightx2v_4steps_lora_v1.1_low_noise:1><lora:|high_noise|wan2.2_t2v_lightx2v_4steps_lora_v1.1_high_noise:1>" --cfg-scale 3.5 --sampling-method euler --steps 4 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 4 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --lora-model-dir ..\..\ComfyUI\models\loras --video-frames 33 --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat<lora:wan2.2_t2v_lightx2v_4steps_lora_v1.1_low_noise:1><lora:|high_noise|wan2.2_t2v_lightx2v_4steps_lora_v1.1_high_noise:1>" --cfg-scale 3.5 --sampling-method euler --steps 4 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 4 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --lora-model-dir ..\..\ComfyUI\models\loras --video-frames 33 --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_14B_t2v_lora.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -114,7 +117,7 @@
|
||||
#### T2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.2_ti2v_5B_fp16.safetensors --vae ..\..\ComfyUI\models\vae\wan2.2_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --offload-to-cpu --video-frames 33 --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.2_ti2v_5B_fp16.safetensors --vae ..\..\ComfyUI\models\vae\wan2.2_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --offload-to-cpu --video-frames 33 --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_5B_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -122,7 +125,7 @@
|
||||
#### I2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.2_ti2v_5B_fp16.safetensors --vae ..\..\ComfyUI\models\vae\wan2.2_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --offload-to-cpu --video-frames 33 -i ..\assets\cat_with_sd_cpp_42.png --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.2_ti2v_5B_fp16.safetensors --vae ..\..\ComfyUI\models\vae\wan2.2_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --offload-to-cpu --video-frames 33 -i ..\assets\cat_with_sd_cpp_42.png --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_5B_i2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -130,7 +133,7 @@
|
||||
### Wan2.1 FLF2V 14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-flf2v-14b-720p-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf --clip_vision ..\..\ComfyUI\models\clip_vision\clip_vision_h.safetensors -p "glass flower blossom" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --video-frames 33 --offload-to-cpu --init-img ..\..\ComfyUI\input\start_image.png --end-img ..\..\ComfyUI\input\end_image.png --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-flf2v-14b-720p-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf --clip_vision ..\..\ComfyUI\models\clip_vision\clip_vision_h.safetensors -p "glass flower blossom" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --video-frames 33 --offload-to-cpu --init-img ..\..\ComfyUI\input\start_image.png --end-img ..\..\ComfyUI\input\end_image.png --flow-shift 3.0
|
||||
```
|
||||
|
||||
|
||||
@@ -139,7 +142,7 @@
|
||||
### Wan2.2 FLF2V 14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -p "glass flower blossom" -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --video-frames 33 --offload-to-cpu --init-img ..\..\ComfyUI\input\start_image.png --end-img ..\..\ComfyUI\input\end_image.png --flow-shift 3.0
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -p "glass flower blossom" -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --video-frames 33 --offload-to-cpu --init-img ..\..\ComfyUI\input\start_image.png --end-img ..\..\ComfyUI\input\end_image.png --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_14B_flf2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -149,7 +152,7 @@
|
||||
#### T2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-vace-1.3b-q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --video-frames 1 --offload-to-cpu
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-vace-1.3b-q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --video-frames 1 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_1.3B_vace_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -158,7 +161,7 @@
|
||||
#### R2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-vace-1.3b-q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa -i ..\assets\cat_with_sd_cpp_42.png --video-frames 33 --offload-to-cpu
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-vace-1.3b-q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa -i ..\assets\cat_with_sd_cpp_42.png --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_1.3B_vace_r2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -169,7 +172,7 @@
|
||||
```
|
||||
mkdir post+depth
|
||||
ffmpeg -i ..\..\ComfyUI\input\post+depth.mp4 -qscale:v 1 -vf fps=8 post+depth\frame_%04d.jpg
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-vace-1.3b-q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "The girl is dancing in a sea of flowers, slowly moving her hands. There is a close - up shot of her upper body. The character is surrounded by other transparent glass flowers in the style of Nicoletta Ceccoli, creating a beautiful, surreal, and emotionally expressive movie scene with a white. transparent feel and a dreamyl atmosphere." --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa -i ..\..\ComfyUI\input\dance_girl.jpg --control-video ./post+depth --video-frames 33 --offload-to-cpu
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-vace-1.3b-q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "The girl is dancing in a sea of flowers, slowly moving her hands. There is a close - up shot of her upper body. The character is surrounded by other transparent glass flowers in the style of Nicoletta Ceccoli, creating a beautiful, surreal, and emotionally expressive movie scene with a white. transparent feel and a dreamyl atmosphere." --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa -i ..\..\ComfyUI\input\dance_girl.jpg --control-video ./post+depth --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_1.3B_vace_v2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -179,7 +182,7 @@ ffmpeg -i ..\..\ComfyUI\input\post+depth.mp4 -qscale:v 1 -vf fps=8 post+depth\fr
|
||||
#### T2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.1_14B_VACE-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --video-frames 33 --offload-to-cpu
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.1_14B_VACE-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_vace_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -188,7 +191,7 @@ ffmpeg -i ..\..\ComfyUI\input\post+depth.mp4 -qscale:v 1 -vf fps=8 post+depth\fr
|
||||
#### R2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.1_14B_VACE-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa -i ..\assets\cat_with_sd_cpp_42.png --video-frames 33 --offload-to-cpu
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.1_14B_VACE-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa -i ..\assets\cat_with_sd_cpp_42.png --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_vace_r2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
@@ -198,7 +201,7 @@ ffmpeg -i ..\..\ComfyUI\input\post+depth.mp4 -qscale:v 1 -vf fps=8 post+depth\fr
|
||||
#### V2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.1_14B_VACE-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "The girl is dancing in a sea of flowers, slowly moving her hands. There is a close - up shot of her upper body. The character is surrounded by other transparent glass flowers in the style of Nicoletta Ceccoli, creating a beautiful, surreal, and emotionally expressive movie scene with a white. transparent feel and a dreamyl atmosphere." --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa -i ..\..\ComfyUI\input\dance_girl.jpg --control-video ./post+depth --video-frames 33 --offload-to-cpu
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.1_14B_VACE-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "The girl is dancing in a sea of flowers, slowly moving her hands. There is a close - up shot of her upper body. The character is surrounded by other transparent glass flowers in the style of Nicoletta Ceccoli, creating a beautiful, surreal, and emotionally expressive movie scene with a white. transparent feel and a dreamyl atmosphere." --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa -i ..\..\ComfyUI\input\dance_girl.jpg --control-video ./post+depth --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_vace_v2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# How to Use
|
||||
|
||||
You can run Z-Image with stable-diffusion.cpp on GPUs with 4GB of VRAM — or even less.
|
||||
|
||||
## Download weights
|
||||
|
||||
- Download Z-Image-Turbo
|
||||
- safetensors: https://huggingface.co/Comfy-Org/z_image_turbo/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/leejet/Z-Image-Turbo-GGUF/tree/main
|
||||
- Download Z-Image
|
||||
- safetensors: https://huggingface.co/Comfy-Org/z_image/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/unsloth/Z-Image-GGUF/tree/main
|
||||
- Download vae
|
||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.1-schnell/tree/main
|
||||
- Download Qwen3 4b
|
||||
- safetensors: https://huggingface.co/Comfy-Org/z_image_turbo/tree/main/split_files/text_encoders
|
||||
- gguf: https://huggingface.co/unsloth/Qwen3-4B-Instruct-2507-GGUF/tree/main
|
||||
|
||||
## Examples
|
||||
|
||||
### Z-Image-Turbo
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model z_image_turbo-Q3_K.gguf --vae ..\..\ComfyUI\models\vae\ae.sft --llm ..\..\ComfyUI\models\text_encoders\Qwen3-4B-Instruct-2507-Q4_K_M.gguf -p "A cinematic, melancholic photograph of a solitary hooded figure walking through a sprawling, rain-slicked metropolis at night. The city lights are a chaotic blur of neon orange and cool blue, reflecting on the wet asphalt. The scene evokes a sense of being a single component in a vast machine. Superimposed over the image in a sleek, modern, slightly glitched font is the philosophical quote: 'THE CITY IS A CIRCUIT BOARD, AND I AM A BROKEN TRANSISTOR.' -- moody, atmospheric, profound, dark academic" --cfg-scale 1.0 -v --offload-to-cpu --diffusion-fa -H 1024 -W 512
|
||||
```
|
||||
|
||||
<img width="256" alt="z-image example" src="../assets/z_image/q3_K.png" />
|
||||
|
||||
### Z-Image-Base
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\z_image_bf16.safetensors --vae ..\..\ComfyUI\models\vae\ae.sft --llm ..\..\ComfyUI\models\text_encoders\qwen_3_4b.safetensors -p "A cinematic, melancholic photograph of a solitary hooded figure walking through a sprawling, rain-slicked metropolis at night. The city lights are a chaotic blur of neon orange and cool blue, reflecting on the wet asphalt. The scene evokes a sense of being a single component in a vast machine. Superimposed over the image in a sleek, modern, slightly glitched font is the philosophical quote: 'THE CITY IS A CIRCUIT BOARD, AND I AM A BROKEN TRANSISTOR.' -- moody, atmospheric, profound, dark academic" --cfg-scale 5.0 -v --offload-to-cpu --diffusion-fa -H 1024 -W 512
|
||||
```
|
||||
|
||||
<img width="256" alt="z-image example" src="../assets/z_image/base_bf16.png" />
|
||||
|
||||
## Comparison of Different Quantization Types
|
||||
|
||||
| bf16 | q8_0 | q6_K | q5_0 | q4_K | q4_0 | q3_K | q2_K|
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| <img width="256" alt="bf16" src="../assets/z_image/bf16.png" /> | <img width="256" alt="q8_0" src="../assets/z_image/q8_0.png" /> | <img width="256" alt="q6_K" src="../assets/z_image/q6_K.png" /> | <img width="256" alt="q5_0" src="../assets/z_image/q5_0.png" /> | <img width="256" alt="q4_K" src="../assets/z_image/q4_K.png" /> | <img width="256" alt="q4_0" src="../assets/z_image/q4_0.png" /> | <img width="256" alt="q3_K" src="../assets/z_image/q3_K.png" /> | <img width="256" alt="q2_K" src="../assets/z_image/q2_K.png" /> |
|
||||
@@ -0,0 +1,265 @@
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "denoiser.hpp"
|
||||
#include "ggml_extend.hpp"
|
||||
|
||||
struct EasyCacheConfig {
|
||||
bool enabled = false;
|
||||
float reuse_threshold = 0.2f;
|
||||
float start_percent = 0.15f;
|
||||
float end_percent = 0.95f;
|
||||
};
|
||||
|
||||
struct EasyCacheCacheEntry {
|
||||
std::vector<float> diff;
|
||||
};
|
||||
|
||||
struct EasyCacheState {
|
||||
EasyCacheConfig config;
|
||||
Denoiser* denoiser = nullptr;
|
||||
float start_sigma = std::numeric_limits<float>::max();
|
||||
float end_sigma = 0.0f;
|
||||
bool initialized = false;
|
||||
bool initial_step = true;
|
||||
bool skip_current_step = false;
|
||||
bool step_active = false;
|
||||
const SDCondition* anchor_condition = nullptr;
|
||||
std::unordered_map<const SDCondition*, EasyCacheCacheEntry> cache_diffs;
|
||||
std::vector<float> prev_input;
|
||||
std::vector<float> prev_output;
|
||||
float output_prev_norm = 0.0f;
|
||||
bool has_prev_input = false;
|
||||
bool has_prev_output = false;
|
||||
bool has_output_prev_norm = false;
|
||||
bool has_relative_transformation_rate = false;
|
||||
float relative_transformation_rate = 0.0f;
|
||||
float cumulative_change_rate = 0.0f;
|
||||
float last_input_change = 0.0f;
|
||||
bool has_last_input_change = false;
|
||||
int total_steps_skipped = 0;
|
||||
int current_step_index = -1;
|
||||
|
||||
void reset_runtime() {
|
||||
initial_step = true;
|
||||
skip_current_step = false;
|
||||
step_active = false;
|
||||
anchor_condition = nullptr;
|
||||
cache_diffs.clear();
|
||||
prev_input.clear();
|
||||
prev_output.clear();
|
||||
output_prev_norm = 0.0f;
|
||||
has_prev_input = false;
|
||||
has_prev_output = false;
|
||||
has_output_prev_norm = false;
|
||||
has_relative_transformation_rate = false;
|
||||
relative_transformation_rate = 0.0f;
|
||||
cumulative_change_rate = 0.0f;
|
||||
last_input_change = 0.0f;
|
||||
has_last_input_change = false;
|
||||
total_steps_skipped = 0;
|
||||
current_step_index = -1;
|
||||
}
|
||||
|
||||
void init(const EasyCacheConfig& cfg, Denoiser* d) {
|
||||
config = cfg;
|
||||
denoiser = d;
|
||||
initialized = cfg.enabled && d != nullptr;
|
||||
reset_runtime();
|
||||
if (initialized) {
|
||||
start_sigma = percent_to_sigma(config.start_percent);
|
||||
end_sigma = percent_to_sigma(config.end_percent);
|
||||
}
|
||||
}
|
||||
|
||||
bool enabled() const {
|
||||
return initialized && config.enabled;
|
||||
}
|
||||
|
||||
float percent_to_sigma(float percent) const {
|
||||
if (!denoiser) {
|
||||
return 0.0f;
|
||||
}
|
||||
if (percent <= 0.0f) {
|
||||
return std::numeric_limits<float>::max();
|
||||
}
|
||||
if (percent >= 1.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
float t = (1.0f - percent) * (TIMESTEPS - 1);
|
||||
return denoiser->t_to_sigma(t);
|
||||
}
|
||||
|
||||
void begin_step(int step_index, float sigma) {
|
||||
if (!enabled()) {
|
||||
return;
|
||||
}
|
||||
if (step_index == current_step_index) {
|
||||
return;
|
||||
}
|
||||
current_step_index = step_index;
|
||||
skip_current_step = false;
|
||||
has_last_input_change = false;
|
||||
step_active = false;
|
||||
if (sigma > start_sigma) {
|
||||
return;
|
||||
}
|
||||
if (!(sigma > end_sigma)) {
|
||||
return;
|
||||
}
|
||||
step_active = true;
|
||||
}
|
||||
|
||||
bool step_is_active() const {
|
||||
return enabled() && step_active;
|
||||
}
|
||||
|
||||
bool is_step_skipped() const {
|
||||
return enabled() && step_active && skip_current_step;
|
||||
}
|
||||
|
||||
bool has_cache(const SDCondition* cond) const {
|
||||
auto it = cache_diffs.find(cond);
|
||||
return it != cache_diffs.end() && !it->second.diff.empty();
|
||||
}
|
||||
|
||||
void update_cache(const SDCondition* cond, ggml_tensor* input, ggml_tensor* output) {
|
||||
EasyCacheCacheEntry& entry = cache_diffs[cond];
|
||||
size_t ne = static_cast<size_t>(ggml_nelements(output));
|
||||
entry.diff.resize(ne);
|
||||
float* out_data = (float*)output->data;
|
||||
float* in_data = (float*)input->data;
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
entry.diff[i] = out_data[i] - in_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
void apply_cache(const SDCondition* cond, ggml_tensor* input, ggml_tensor* output) {
|
||||
auto it = cache_diffs.find(cond);
|
||||
if (it == cache_diffs.end() || it->second.diff.empty()) {
|
||||
return;
|
||||
}
|
||||
copy_ggml_tensor(output, input);
|
||||
float* out_data = (float*)output->data;
|
||||
const std::vector<float>& diff = it->second.diff;
|
||||
for (size_t i = 0; i < diff.size(); ++i) {
|
||||
out_data[i] += diff[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool before_condition(const SDCondition* cond,
|
||||
ggml_tensor* input,
|
||||
ggml_tensor* output,
|
||||
float sigma,
|
||||
int step_index) {
|
||||
if (!enabled() || step_index < 0) {
|
||||
return false;
|
||||
}
|
||||
if (step_index != current_step_index) {
|
||||
begin_step(step_index, sigma);
|
||||
}
|
||||
if (!step_active) {
|
||||
return false;
|
||||
}
|
||||
if (initial_step) {
|
||||
anchor_condition = cond;
|
||||
initial_step = false;
|
||||
}
|
||||
bool is_anchor = (cond == anchor_condition);
|
||||
if (skip_current_step) {
|
||||
if (has_cache(cond)) {
|
||||
apply_cache(cond, input, output);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!is_anchor) {
|
||||
return false;
|
||||
}
|
||||
if (!has_prev_input || !has_prev_output || !has_cache(cond)) {
|
||||
return false;
|
||||
}
|
||||
size_t ne = static_cast<size_t>(ggml_nelements(input));
|
||||
if (prev_input.size() != ne) {
|
||||
return false;
|
||||
}
|
||||
float* input_data = (float*)input->data;
|
||||
last_input_change = 0.0f;
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
last_input_change += std::fabs(input_data[i] - prev_input[i]);
|
||||
}
|
||||
if (ne > 0) {
|
||||
last_input_change /= static_cast<float>(ne);
|
||||
}
|
||||
has_last_input_change = true;
|
||||
|
||||
if (has_output_prev_norm && has_relative_transformation_rate && last_input_change > 0.0f && output_prev_norm > 0.0f) {
|
||||
float approx_output_change_rate = (relative_transformation_rate * last_input_change) / output_prev_norm;
|
||||
cumulative_change_rate += approx_output_change_rate;
|
||||
if (cumulative_change_rate < config.reuse_threshold) {
|
||||
skip_current_step = true;
|
||||
total_steps_skipped++;
|
||||
apply_cache(cond, input, output);
|
||||
return true;
|
||||
} else {
|
||||
cumulative_change_rate = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void after_condition(const SDCondition* cond, ggml_tensor* input, ggml_tensor* output) {
|
||||
if (!step_is_active()) {
|
||||
return;
|
||||
}
|
||||
update_cache(cond, input, output);
|
||||
if (cond != anchor_condition) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t ne = static_cast<size_t>(ggml_nelements(input));
|
||||
float* in_data = (float*)input->data;
|
||||
prev_input.resize(ne);
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
prev_input[i] = in_data[i];
|
||||
}
|
||||
has_prev_input = true;
|
||||
|
||||
float* out_data = (float*)output->data;
|
||||
float output_change = 0.0f;
|
||||
if (has_prev_output && prev_output.size() == ne) {
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
output_change += std::fabs(out_data[i] - prev_output[i]);
|
||||
}
|
||||
if (ne > 0) {
|
||||
output_change /= static_cast<float>(ne);
|
||||
}
|
||||
}
|
||||
|
||||
prev_output.resize(ne);
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
prev_output[i] = out_data[i];
|
||||
}
|
||||
has_prev_output = true;
|
||||
|
||||
float mean_abs = 0.0f;
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
mean_abs += std::fabs(out_data[i]);
|
||||
}
|
||||
output_prev_norm = (ne > 0) ? (mean_abs / static_cast<float>(ne)) : 0.0f;
|
||||
has_output_prev_norm = output_prev_norm > 0.0f;
|
||||
|
||||
if (has_last_input_change && last_input_change > 0.0f && output_change > 0.0f) {
|
||||
float rate = output_change / last_input_change;
|
||||
if (std::isfinite(rate)) {
|
||||
relative_transformation_rate = rate;
|
||||
has_relative_transformation_rate = true;
|
||||
}
|
||||
}
|
||||
cumulative_change_rate = 0.0f;
|
||||
has_last_input_change = false;
|
||||
}
|
||||
};
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
x_cat = ggml_concat(ctx->ggml_ctx, x_cat, x4, 2);
|
||||
auto x5 = conv5->forward(ctx, x_cat);
|
||||
|
||||
x5 = ggml_add(ctx->ggml_ctx, ggml_scale(ctx->ggml_ctx, x5, 0.2f), x);
|
||||
x5 = ggml_add(ctx->ggml_ctx, ggml_ext_scale(ctx->ggml_ctx, x5, 0.2f), x);
|
||||
return x5;
|
||||
}
|
||||
};
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
out = rdb2->forward(ctx, out);
|
||||
out = rdb3->forward(ctx, out);
|
||||
|
||||
out = ggml_add(ctx->ggml_ctx, ggml_scale(ctx->ggml_ctx, out, 0.2f), x);
|
||||
out = ggml_add(ctx->ggml_ctx, ggml_ext_scale(ctx->ggml_ctx, out, 0.2f), x);
|
||||
return out;
|
||||
}
|
||||
};
|
||||
@@ -156,9 +156,10 @@ struct ESRGAN : public GGMLRunner {
|
||||
|
||||
ESRGAN(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
int tile_size = 128,
|
||||
const String2TensorStorage& tensor_storage_map = {})
|
||||
: GGMLRunner(backend, offload_params_to_cpu) {
|
||||
// rrdb_net will be created in load_from_file
|
||||
this->tile_size = tile_size;
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
@@ -344,7 +345,7 @@ struct ESRGAN : public GGMLRunner {
|
||||
if (!rrdb_net)
|
||||
return nullptr;
|
||||
constexpr int kGraphNodes = 1 << 16; // 65k
|
||||
struct ggml_cgraph* gf = ggml_new_graph_custom(compute_ctx, kGraphNodes, /*grads*/ false);
|
||||
struct ggml_cgraph* gf = new_graph_custom(kGraphNodes);
|
||||
x = to_backend(x);
|
||||
|
||||
auto runner_ctx = get_context();
|
||||
@@ -353,14 +354,14 @@ struct ESRGAN : public GGMLRunner {
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(const int n_threads,
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* x,
|
||||
ggml_tensor** output,
|
||||
ggml_context* output_ctx = nullptr) {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(x);
|
||||
};
|
||||
GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
return GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_subdirectory(cli)
|
||||
add_subdirectory(cli)
|
||||
add_subdirectory(server)
|
||||
@@ -1,4 +1,4 @@
|
||||
set(TARGET sd)
|
||||
set(TARGET sd-cli)
|
||||
|
||||
add_executable(${TARGET} main.cpp)
|
||||
install(TARGETS ${TARGET} RUNTIME)
|
||||
|
||||
@@ -1,42 +1,93 @@
|
||||
# Run
|
||||
|
||||
```
|
||||
usage: ./bin/sd [options]
|
||||
usage: ./bin/sd-cli [options]
|
||||
|
||||
Options:
|
||||
CLI Options:
|
||||
-o, --output <string> path to write result image to. you can use printf-style %d format specifiers for image sequences (default: ./output.png) (eg. output_%03d.png)
|
||||
--output-begin-idx <int> starting index for output image sequence, must be non-negative (default 0 if specified %d in output path, 1 otherwise)
|
||||
--preview-path <string> path to write preview image to (default: ./preview.png)
|
||||
--preview-interval <int> interval in denoising steps between consecutive updates of the image preview file (default is 1, meaning updating at
|
||||
every step)
|
||||
--canny apply canny preprocessor (edge detection)
|
||||
--convert-name convert tensor name (for convert mode)
|
||||
-v, --verbose print extra info
|
||||
--color colors the logging tags according to level
|
||||
--taesd-preview-only prevents usage of taesd for decoding the final image. (for use with --preview tae)
|
||||
--preview-noisy enables previewing noisy inputs of the models rather than the denoised outputs
|
||||
-M, --mode run mode, one of [img_gen, vid_gen, upscale, convert], default: img_gen
|
||||
--preview preview method. must be one of the following [none, proj, tae, vae] (default is none)
|
||||
-h, --help show this help message and exit
|
||||
|
||||
Context Options:
|
||||
-m, --model <string> path to full model
|
||||
--clip_l <string> path to the clip-l text encoder
|
||||
--clip_g <string> path to the clip-g text encoder
|
||||
--clip_vision <string> path to the clip-vision encoder
|
||||
--t5xxl <string> path to the t5xxl text encoder
|
||||
--qwen2vl <string> path to the qwen2vl text encoder
|
||||
--qwen2vl_vision <string> path to the qwen2vl vit
|
||||
--llm <string> path to the llm text encoder. For example: (qwenvl2.5 for qwen-image, mistral-small3.2 for flux2, ...)
|
||||
--llm_vision <string> path to the llm vit
|
||||
--qwen2vl <string> alias of --llm. Deprecated.
|
||||
--qwen2vl_vision <string> alias of --llm_vision. Deprecated.
|
||||
--diffusion-model <string> path to the standalone diffusion model
|
||||
--high-noise-diffusion-model <string> path to the standalone high noise diffusion model
|
||||
--vae <string> path to standalone vae model
|
||||
--taesd <string> path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
|
||||
--tae <string> alias of --taesd
|
||||
--control-net <string> path to control net model
|
||||
--embd-dir <string> embeddings directory
|
||||
--lora-model-dir <string> lora model directory
|
||||
-i, --init-img <string> path to the init image
|
||||
--end-img <string> path to the end image, required by flf2v
|
||||
--tensor-type-rules <string> weight type per tensor pattern (example: "^vae\.=f16,model\.=q8_0")
|
||||
--photo-maker <string> path to PHOTOMAKER model
|
||||
--pm-id-images-dir <string> path to PHOTOMAKER input id images dir
|
||||
--pm-id-embed-path <string> path to PHOTOMAKER v2 id embed
|
||||
--upscale-model <string> path to esrgan model.
|
||||
-t, --threads <int> number of threads to use during computation (default: -1). If threads <= 0, then threads will be set to the number of
|
||||
CPU physical cores
|
||||
--chroma-t5-mask-pad <int> t5 mask pad size of chroma
|
||||
--vae-tile-overlap <float> tile overlap for vae tiling, in fraction of tile size (default: 0.5)
|
||||
--flow-shift <float> shift value for Flow models like SD3.x or WAN (default: auto)
|
||||
--vae-tiling process vae in tiles to reduce memory usage
|
||||
--force-sdxl-vae-conv-scale force use of conv scale on sdxl vae
|
||||
--offload-to-cpu place the weights in RAM to save VRAM, and automatically load them into VRAM when needed
|
||||
--mmap whether to memory-map model
|
||||
--control-net-cpu keep controlnet in cpu (for low vram)
|
||||
--clip-on-cpu keep clip in cpu (for low vram)
|
||||
--vae-on-cpu keep vae in cpu (for low vram)
|
||||
--fa use flash attention
|
||||
--diffusion-fa use flash attention in the diffusion model only
|
||||
--diffusion-conv-direct use ggml_conv2d_direct in the diffusion model
|
||||
--vae-conv-direct use ggml_conv2d_direct in the vae model
|
||||
--circular enable circular padding for convolutions
|
||||
--circularx enable circular RoPE wrapping on x-axis (width) only
|
||||
--circulary enable circular RoPE wrapping on y-axis (height) only
|
||||
--chroma-disable-dit-mask disable dit mask for chroma
|
||||
--chroma-enable-t5-mask enable t5 mask for chroma
|
||||
--type weight type (examples: f32, f16, q4_0, q4_1, q5_0, q5_1, q8_0, q2_K, q3_K, q4_K). If not specified, the default is the
|
||||
type of the weight file
|
||||
--rng RNG, one of [std_default, cuda, cpu], default: cuda(sd-webui), cpu(comfyui)
|
||||
--sampler-rng sampler RNG, one of [std_default, cuda, cpu]. If not specified, use --rng
|
||||
--prediction prediction type override, one of [eps, v, edm_v, sd3_flow, flux_flow, flux2_flow]
|
||||
--lora-apply-mode the way to apply LoRA, one of [auto, immediately, at_runtime], default is auto. In auto mode, if the model weights
|
||||
contain any quantized parameters, the at_runtime mode will be used; otherwise,
|
||||
immediately will be used.The immediately mode may have precision and
|
||||
compatibility issues with quantized parameters, but it usually offers faster inference
|
||||
speed and, in some cases, lower memory usage. The at_runtime mode, on the
|
||||
other hand, is exactly the opposite.
|
||||
--vae-tile-size tile size for vae tiling, format [X]x[Y] (default: 32x32)
|
||||
--vae-relative-tile-size relative tile size for vae tiling, format [X]x[Y], in fraction of image size if < 1, in number of tiles per dim if >=1
|
||||
(overrides --vae-tile-size)
|
||||
|
||||
Generation Options:
|
||||
-p, --prompt <string> the prompt to render
|
||||
-n, --negative-prompt <string> the negative prompt (default: "")
|
||||
-i, --init-img <string> path to the init image
|
||||
--end-img <string> path to the end image, required by flf2v
|
||||
--mask <string> path to the mask image
|
||||
--control-image <string> path to control image, control net
|
||||
--control-video <string> path to control video frames, It must be a directory path. The video frames inside should be stored as images in
|
||||
lexicographical (character) order. For example, if the control video path is
|
||||
`frames`, the directory contain images such as 00.png, 01.png, ... etc.
|
||||
-o, --output <string> path to write result image to (default: ./output.png)
|
||||
-p, --prompt <string> the prompt to render
|
||||
-n, --negative-prompt <string> the negative prompt (default: "")
|
||||
--preview-path <string> path to write preview image to (default: ./preview.png)
|
||||
--upscale-model <string> path to esrgan model.
|
||||
-t, --threads <int> number of threads to use during computation (default: -1). If threads <= 0, then threads will be set to the number of
|
||||
CPU physical cores
|
||||
--upscale-repeats <int> Run the ESRGAN upscaler this many times (default: 1)
|
||||
--pm-id-images-dir <string> path to PHOTOMAKER input id images dir
|
||||
--pm-id-embed-path <string> path to PHOTOMAKER v2 id embed
|
||||
-H, --height <int> image height, in pixel space (default: 512)
|
||||
-W, --width <int> image width, in pixel space (default: 512)
|
||||
--steps <int> number of sample steps (default: 20)
|
||||
@@ -44,13 +95,12 @@ Options:
|
||||
--clip-skip <int> ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer (default: -1). <= 0 represents unspecified,
|
||||
will be 1 for SD1.x, 2 for SD2.x
|
||||
-b, --batch-count <int> batch count
|
||||
--chroma-t5-mask-pad <int> t5 mask pad size of chroma
|
||||
--video-frames <int> video frames (default: 1)
|
||||
--fps <int> fps (default: 24)
|
||||
--timestep-shift <int> shift timestep for NitroFusion models (default: 0). recommended N for NitroSD-Realism around 250 and 500 for
|
||||
NitroSD-Vibrant
|
||||
--preview-interval <int> interval in denoising steps between consecutive updates of the image preview file (default is 1, meaning updating at
|
||||
every step)
|
||||
--upscale-repeats <int> Run the ESRGAN upscaler this many times (default: 1)
|
||||
--upscale-tile-size <int> tile size for ESRGAN upscaling (default: 128)
|
||||
--cfg-scale <float> unconditional guidance scale: (default: 7.0)
|
||||
--img-cfg-scale <float> image guidance scale for inpaint or instruct-pix2pix models: (default: same as --cfg-scale)
|
||||
--guidance <float> distilled guidance scale for models with guidance input (default: 3.5)
|
||||
@@ -58,59 +108,37 @@ Options:
|
||||
medium
|
||||
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
||||
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
||||
--eta <float> eta in DDIM, only for DDIM and TCD (default: 0)
|
||||
--eta <float> eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0)
|
||||
--high-noise-cfg-scale <float> (high noise) unconditional guidance scale: (default: 7.0)
|
||||
--high-noise-img-cfg-scale <float> (high noise) image guidance scale for inpaint or instruct-pix2pix models (default: same as --cfg-scale)
|
||||
--high-noise-guidance <float> (high noise) distilled guidance scale for models with guidance input (default: 3.5)
|
||||
--high-noise-slg-scale <float> (high noise) skip layer guidance (SLG) scale, only for DiT models: (default: 0)
|
||||
--high-noise-skip-layer-start <float> (high noise) SLG enabling point (default: 0.01)
|
||||
--high-noise-skip-layer-end <float> (high noise) SLG disabling point (default: 0.2)
|
||||
--high-noise-eta <float> (high noise) eta in DDIM, only for DDIM and TCD (default: 0)
|
||||
--high-noise-eta <float> (high noise) eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0)
|
||||
--strength <float> strength for noising/unnoising (default: 0.75)
|
||||
--pm-style-strength <float>
|
||||
--control-strength <float> strength to apply Control Net (default: 0.9). 1.0 corresponds to full destruction of information in init image
|
||||
--moe-boundary <float> timestep boundary for Wan2.2 MoE model. (default: 0.875). Only enabled if `--high-noise-steps` is set to -1
|
||||
--flow-shift <float> shift value for Flow models like SD3.x or WAN (default: auto)
|
||||
--vace-strength <float> wan vace strength
|
||||
--vae-tile-overlap <float> tile overlap for vae tiling, in fraction of tile size (default: 0.5)
|
||||
--vae-tiling process vae in tiles to reduce memory usage
|
||||
--force-sdxl-vae-conv-scale force use of conv scale on sdxl vae
|
||||
--offload-to-cpu place the weights in RAM to save VRAM, and automatically load them into VRAM when needed
|
||||
--control-net-cpu keep controlnet in cpu (for low vram)
|
||||
--clip-on-cpu keep clip in cpu (for low vram)
|
||||
--vae-on-cpu keep vae in cpu (for low vram)
|
||||
--diffusion-fa use flash attention in the diffusion model
|
||||
--diffusion-conv-direct use ggml_conv2d_direct in the diffusion model
|
||||
--vae-conv-direct use ggml_conv2d_direct in the vae model
|
||||
--canny apply canny preprocessor (edge detection)
|
||||
-v, --verbose print extra info
|
||||
--color colors the logging tags according to level
|
||||
--chroma-disable-dit-mask disable dit mask for chroma
|
||||
--chroma-enable-t5-mask enable t5 mask for chroma
|
||||
--increase-ref-index automatically increase the indices of references images based on the order they are listed (starting with 1).
|
||||
--disable-auto-resize-ref-image disable auto resize of ref images
|
||||
--taesd-preview-only prevents usage of taesd for decoding the final image. (for use with --preview tae)
|
||||
--preview-noisy enables previewing noisy inputs of the models rather than the denoised outputs
|
||||
-M, --mode run mode, one of [img_gen, vid_gen, upscale, convert], default: img_gen
|
||||
--type weight type (examples: f32, f16, q4_0, q4_1, q5_0, q5_1, q8_0, q2_K, q3_K, q4_K). If not specified, the default is the
|
||||
type of the weight file
|
||||
--rng RNG, one of [std_default, cuda], default: cuda
|
||||
-s, --seed RNG seed (default: 42, use random seed for < 0)
|
||||
--sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing,
|
||||
tcd] (default: euler for Flux/SD3/Wan, euler_a otherwise)
|
||||
--prediction prediction type override, one of [eps, v, edm_v, sd3_flow, flux_flow]
|
||||
--scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple], default:
|
||||
discrete
|
||||
--sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd,
|
||||
res_multistep, res_2s] (default: euler for Flux/SD3/Wan, euler_a otherwise)
|
||||
--high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing,
|
||||
tcd, res_multistep, res_2s] default: euler for Flux/SD3/Wan, euler_a otherwise
|
||||
--scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple,
|
||||
kl_optimal, lcm, bong_tangent], default: discrete
|
||||
--sigmas custom sigma values for the sampler, comma-separated (e.g., "14.61,7.8,3.5,0.0").
|
||||
--skip-layers layers to skip for SLG steps (default: [7,8,9])
|
||||
--high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm,
|
||||
ddim_trailing, tcd] default: euler for Flux/SD3/Wan, euler_a otherwise
|
||||
--high-noise-scheduler (high noise) denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform,
|
||||
simple], default: discrete
|
||||
--high-noise-skip-layers (high noise) layers to skip for SLG steps (default: [7,8,9])
|
||||
-r, --ref-image reference image for Flux Kontext models (can be used multiple times)
|
||||
-h, --help show this help message and exit
|
||||
--vae-tile-size tile size for vae tiling, format [X]x[Y] (default: 32x32)
|
||||
--vae-relative-tile-size relative tile size for vae tiling, format [X]x[Y], in fraction of image size if < 1, in number of tiles per dim if >=1
|
||||
(overrides --vae-tile-size)
|
||||
--preview preview method. must be one of the following [none, proj, tae, vae] (default is none)
|
||||
```
|
||||
--cache-mode caching method: 'easycache' (DiT), 'ucache' (UNET), 'dbcache'/'taylorseer'/'cache-dit' (DiT block-level)
|
||||
--cache-option named cache params (key=value format, comma-separated). easycache/ucache:
|
||||
threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit: Fn=,Bn=,threshold=,warmup=. Examples:
|
||||
"threshold=0.25" or "threshold=1.5,reset=0"
|
||||
--cache-preset cache-dit preset: 'slow'/'s', 'medium'/'m', 'fast'/'f', 'ultra'/'u'
|
||||
--scm-mask SCM steps mask for cache-dit: comma-separated 0/1 (e.g., "1,1,1,0,0,1,0,0,1,0") - 1=compute, 0=can cache
|
||||
--scm-policy SCM policy: 'dynamic' (default) or 'static'
|
||||
```
|
||||
|
||||
@@ -172,9 +172,9 @@ int create_mjpg_avi_from_sd_images(const char* filename, sd_image_t* images, int
|
||||
|
||||
// Write '00dc' chunk (video frame)
|
||||
fwrite("00dc", 4, 1, f);
|
||||
write_u32_le(f, jpeg_data.size);
|
||||
write_u32_le(f, (uint32_t)jpeg_data.size);
|
||||
index[i].offset = ftell(f) - 8;
|
||||
index[i].size = jpeg_data.size;
|
||||
index[i].size = (uint32_t)jpeg_data.size;
|
||||
fwrite(jpeg_data.buf, 1, jpeg_data.size, f);
|
||||
|
||||
// Align to even byte size
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
set(TARGET sd-server)
|
||||
|
||||
add_executable(${TARGET} main.cpp)
|
||||
install(TARGETS ${TARGET} RUNTIME)
|
||||
target_link_libraries(${TARGET} PRIVATE stable-diffusion ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_compile_features(${TARGET} PUBLIC c_std_11 cxx_std_17)
|
||||
@@ -0,0 +1,136 @@
|
||||
# Run
|
||||
|
||||
```
|
||||
usage: ./bin/sd-server [options]
|
||||
|
||||
Svr Options:
|
||||
-l, --listen-ip <string> server listen ip (default: 127.0.0.1)
|
||||
--listen-port <int> server listen port (default: 1234)
|
||||
--serve-html-path <string> path to HTML file to serve at root (optional)
|
||||
-v, --verbose print extra info
|
||||
--color colors the logging tags according to level
|
||||
-h, --help show this help message and exit
|
||||
|
||||
Context Options:
|
||||
-m, --model <string> path to full model
|
||||
--clip_l <string> path to the clip-l text encoder
|
||||
--clip_g <string> path to the clip-g text encoder
|
||||
--clip_vision <string> path to the clip-vision encoder
|
||||
--t5xxl <string> path to the t5xxl text encoder
|
||||
--llm <string> path to the llm text encoder. For example: (qwenvl2.5 for qwen-image, mistral-small3.2 for flux2, ...)
|
||||
--llm_vision <string> path to the llm vit
|
||||
--qwen2vl <string> alias of --llm. Deprecated.
|
||||
--qwen2vl_vision <string> alias of --llm_vision. Deprecated.
|
||||
--diffusion-model <string> path to the standalone diffusion model
|
||||
--high-noise-diffusion-model <string> path to the standalone high noise diffusion model
|
||||
--vae <string> path to standalone vae model
|
||||
--taesd <string> path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
|
||||
--tae <string> alias of --taesd
|
||||
--control-net <string> path to control net model
|
||||
--embd-dir <string> embeddings directory
|
||||
--lora-model-dir <string> lora model directory
|
||||
--tensor-type-rules <string> weight type per tensor pattern (example: "^vae\.=f16,model\.=q8_0")
|
||||
--photo-maker <string> path to PHOTOMAKER model
|
||||
--upscale-model <string> path to esrgan model.
|
||||
-t, --threads <int> number of threads to use during computation (default: -1). If threads <= 0, then threads will be set to the number of
|
||||
CPU physical cores
|
||||
--chroma-t5-mask-pad <int> t5 mask pad size of chroma
|
||||
--vae-tile-overlap <float> tile overlap for vae tiling, in fraction of tile size (default: 0.5)
|
||||
--flow-shift <float> shift value for Flow models like SD3.x or WAN (default: auto)
|
||||
--vae-tiling process vae in tiles to reduce memory usage
|
||||
--force-sdxl-vae-conv-scale force use of conv scale on sdxl vae
|
||||
--offload-to-cpu place the weights in RAM to save VRAM, and automatically load them into VRAM when needed
|
||||
--control-net-cpu keep controlnet in cpu (for low vram)
|
||||
--clip-on-cpu keep clip in cpu (for low vram)
|
||||
--vae-on-cpu keep vae in cpu (for low vram)
|
||||
--mmap whether to memory-map model
|
||||
--fa use flash attention
|
||||
--diffusion-fa use flash attention in the diffusion model only
|
||||
--diffusion-conv-direct use ggml_conv2d_direct in the diffusion model
|
||||
--vae-conv-direct use ggml_conv2d_direct in the vae model
|
||||
--circular enable circular padding for convolutions
|
||||
--circularx enable circular RoPE wrapping on x-axis (width) only
|
||||
--circulary enable circular RoPE wrapping on y-axis (height) only
|
||||
--chroma-disable-dit-mask disable dit mask for chroma
|
||||
--chroma-enable-t5-mask enable t5 mask for chroma
|
||||
--type weight type (examples: f32, f16, q4_0, q4_1, q5_0, q5_1, q8_0, q2_K, q3_K, q4_K). If not specified, the default is the
|
||||
type of the weight file
|
||||
--rng RNG, one of [std_default, cuda, cpu], default: cuda(sd-webui), cpu(comfyui)
|
||||
--sampler-rng sampler RNG, one of [std_default, cuda, cpu]. If not specified, use --rng
|
||||
--prediction prediction type override, one of [eps, v, edm_v, sd3_flow, flux_flow, flux2_flow]
|
||||
--lora-apply-mode the way to apply LoRA, one of [auto, immediately, at_runtime], default is auto. In auto mode, if the model weights
|
||||
contain any quantized parameters, the at_runtime mode will be used; otherwise,
|
||||
immediately will be used.The immediately mode may have precision and
|
||||
compatibility issues with quantized parameters, but it usually offers faster inference
|
||||
speed and, in some cases, lower memory usage. The at_runtime mode, on the
|
||||
other hand, is exactly the opposite.
|
||||
--vae-tile-size tile size for vae tiling, format [X]x[Y] (default: 32x32)
|
||||
--vae-relative-tile-size relative tile size for vae tiling, format [X]x[Y], in fraction of image size if < 1, in number of tiles per dim if >=1
|
||||
(overrides --vae-tile-size)
|
||||
|
||||
Default Generation Options:
|
||||
-p, --prompt <string> the prompt to render
|
||||
-n, --negative-prompt <string> the negative prompt (default: "")
|
||||
-i, --init-img <string> path to the init image
|
||||
--end-img <string> path to the end image, required by flf2v
|
||||
--mask <string> path to the mask image
|
||||
--control-image <string> path to control image, control net
|
||||
--control-video <string> path to control video frames, It must be a directory path. The video frames inside should be stored as images in
|
||||
lexicographical (character) order. For example, if the control video path is
|
||||
`frames`, the directory contain images such as 00.png, 01.png, ... etc.
|
||||
--pm-id-images-dir <string> path to PHOTOMAKER input id images dir
|
||||
--pm-id-embed-path <string> path to PHOTOMAKER v2 id embed
|
||||
-H, --height <int> image height, in pixel space (default: 512)
|
||||
-W, --width <int> image width, in pixel space (default: 512)
|
||||
--steps <int> number of sample steps (default: 20)
|
||||
--high-noise-steps <int> (high noise) number of sample steps (default: -1 = auto)
|
||||
--clip-skip <int> ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer (default: -1). <= 0 represents unspecified,
|
||||
will be 1 for SD1.x, 2 for SD2.x
|
||||
-b, --batch-count <int> batch count
|
||||
--video-frames <int> video frames (default: 1)
|
||||
--fps <int> fps (default: 24)
|
||||
--timestep-shift <int> shift timestep for NitroFusion models (default: 0). recommended N for NitroSD-Realism around 250 and 500 for
|
||||
NitroSD-Vibrant
|
||||
--upscale-repeats <int> Run the ESRGAN upscaler this many times (default: 1)
|
||||
--upscale-tile-size <int> tile size for ESRGAN upscaling (default: 128)
|
||||
--cfg-scale <float> unconditional guidance scale: (default: 7.0)
|
||||
--img-cfg-scale <float> image guidance scale for inpaint or instruct-pix2pix models: (default: same as --cfg-scale)
|
||||
--guidance <float> distilled guidance scale for models with guidance input (default: 3.5)
|
||||
--slg-scale <float> skip layer guidance (SLG) scale, only for DiT models: (default: 0). 0 means disabled, a value of 2.5 is nice for sd3.5
|
||||
medium
|
||||
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
||||
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
||||
--eta <float> eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0)
|
||||
--high-noise-cfg-scale <float> (high noise) unconditional guidance scale: (default: 7.0)
|
||||
--high-noise-img-cfg-scale <float> (high noise) image guidance scale for inpaint or instruct-pix2pix models (default: same as --cfg-scale)
|
||||
--high-noise-guidance <float> (high noise) distilled guidance scale for models with guidance input (default: 3.5)
|
||||
--high-noise-slg-scale <float> (high noise) skip layer guidance (SLG) scale, only for DiT models: (default: 0)
|
||||
--high-noise-skip-layer-start <float> (high noise) SLG enabling point (default: 0.01)
|
||||
--high-noise-skip-layer-end <float> (high noise) SLG disabling point (default: 0.2)
|
||||
--high-noise-eta <float> (high noise) eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0)
|
||||
--strength <float> strength for noising/unnoising (default: 0.75)
|
||||
--pm-style-strength <float>
|
||||
--control-strength <float> strength to apply Control Net (default: 0.9). 1.0 corresponds to full destruction of information in init image
|
||||
--moe-boundary <float> timestep boundary for Wan2.2 MoE model. (default: 0.875). Only enabled if `--high-noise-steps` is set to -1
|
||||
--vace-strength <float> wan vace strength
|
||||
--increase-ref-index automatically increase the indices of references images based on the order they are listed (starting with 1).
|
||||
--disable-auto-resize-ref-image disable auto resize of ref images
|
||||
-s, --seed RNG seed (default: 42, use random seed for < 0)
|
||||
--sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd,
|
||||
res_multistep, res_2s] (default: euler for Flux/SD3/Wan, euler_a otherwise)
|
||||
--high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing,
|
||||
tcd, res_multistep, res_2s] default: euler for Flux/SD3/Wan, euler_a otherwise
|
||||
--scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple,
|
||||
kl_optimal, lcm, bong_tangent], default: discrete
|
||||
--sigmas custom sigma values for the sampler, comma-separated (e.g., "14.61,7.8,3.5,0.0").
|
||||
--skip-layers layers to skip for SLG steps (default: [7,8,9])
|
||||
--high-noise-skip-layers (high noise) layers to skip for SLG steps (default: [7,8,9])
|
||||
-r, --ref-image reference image for Flux Kontext models (can be used multiple times)
|
||||
--cache-mode caching method: 'easycache' (DiT), 'ucache' (UNET), 'dbcache'/'taylorseer'/'cache-dit' (DiT block-level)
|
||||
--cache-option named cache params (key=value format, comma-separated). easycache/ucache:
|
||||
threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit: Fn=,Bn=,threshold=,warmup=. Examples:
|
||||
"threshold=0.25" or "threshold=1.5,reset=0"
|
||||
--cache-preset cache-dit preset: 'slow'/'s', 'medium'/'m', 'fast'/'f', 'ultra'/'u'
|
||||
--scm-mask SCM steps mask for cache-dit: comma-separated 0/1 (e.g., "1,1,1,0,0,1,0,0,1,0") - 1=compute, 0=can cache
|
||||
--scm-policy SCM policy: 'dynamic' (default) or 'static'
|
||||
```
|
||||
@@ -1,4 +1,4 @@
|
||||
for f in *.cpp *.h *.hpp examples/cli/*.cpp examples/cli/*.h; do
|
||||
for f in *.cpp *.h *.hpp examples/cli/*.cpp examples/common/*.hpp examples/cli/*.h examples/server/*.cpp; do
|
||||
[[ "$f" == vocab* ]] && continue
|
||||
echo "formatting '$f'"
|
||||
# if [ "$f" != "stable-diffusion.h" ]; then
|
||||
|
||||
@@ -151,7 +151,7 @@ private:
|
||||
}
|
||||
|
||||
if (n_dims > GGML_MAX_DIMS) {
|
||||
for (int i = GGML_MAX_DIMS; i < n_dims; i++) {
|
||||
for (uint32_t i = GGML_MAX_DIMS; i < n_dims; i++) {
|
||||
info.shape[GGML_MAX_DIMS - 1] *= info.shape[i]; // stack to last dim;
|
||||
}
|
||||
info.shape.resize(GGML_MAX_DIMS);
|
||||
|
||||
@@ -91,6 +91,41 @@ const float flux_latent_rgb_proj[16][3] = {
|
||||
{-0.111849f, -0.055589f, -0.032361f}};
|
||||
float flux_latent_rgb_bias[3] = {0.024600f, -0.006937f, -0.008089f};
|
||||
|
||||
const float flux2_latent_rgb_proj[32][3] = {
|
||||
{0.000736f, -0.008385f, -0.019710f},
|
||||
{-0.001352f, -0.016392f, 0.020693f},
|
||||
{-0.006376f, 0.002428f, 0.036736f},
|
||||
{0.039384f, 0.074167f, 0.119789f},
|
||||
{0.007464f, -0.005705f, -0.004734f},
|
||||
{-0.004086f, 0.005287f, -0.000409f},
|
||||
{-0.032835f, 0.050802f, -0.028120f},
|
||||
{-0.003158f, -0.000835f, 0.000406f},
|
||||
{-0.112840f, -0.084337f, -0.023083f},
|
||||
{0.001462f, -0.006656f, 0.000549f},
|
||||
{-0.009980f, -0.007480f, 0.009702f},
|
||||
{0.032540f, 0.000214f, -0.061388f},
|
||||
{0.011023f, 0.000694f, 0.007143f},
|
||||
{-0.001468f, -0.006723f, -0.001678f},
|
||||
{-0.005921f, -0.010320f, -0.003907f},
|
||||
{-0.028434f, 0.027584f, 0.018457f},
|
||||
{0.014349f, 0.011523f, 0.000441f},
|
||||
{0.009874f, 0.003081f, 0.001507f},
|
||||
{0.002218f, 0.005712f, 0.001563f},
|
||||
{0.053010f, -0.019844f, 0.008683f},
|
||||
{-0.002507f, 0.005384f, 0.000938f},
|
||||
{-0.002177f, -0.011366f, 0.003559f},
|
||||
{-0.000261f, 0.015121f, -0.003240f},
|
||||
{-0.003944f, -0.002083f, 0.005043f},
|
||||
{-0.009138f, 0.011336f, 0.003781f},
|
||||
{0.011429f, 0.003985f, -0.003855f},
|
||||
{0.010518f, -0.005586f, 0.010131f},
|
||||
{0.007883f, 0.002912f, -0.001473f},
|
||||
{-0.003318f, -0.003160f, 0.003684f},
|
||||
{-0.034560f, -0.008740f, 0.012996f},
|
||||
{0.000166f, 0.001079f, -0.012153f},
|
||||
{0.017772f, 0.000937f, -0.011953f}};
|
||||
float flux2_latent_rgb_bias[3] = {-0.028738f, -0.098463f, -0.107619f};
|
||||
|
||||
// This one was taken straight from
|
||||
// https://github.com/Stability-AI/sd3.5/blob/8565799a3b41eb0c7ba976d18375f0f753f56402/sd3_impls.py#L288-L303
|
||||
// (MiT Licence)
|
||||
@@ -128,16 +163,42 @@ const float sd_latent_rgb_proj[4][3] = {
|
||||
{-0.178022f, -0.200862f, -0.678514f}};
|
||||
float sd_latent_rgb_bias[3] = {-0.017478f, -0.055834f, -0.105825f};
|
||||
|
||||
void preview_latent_video(uint8_t* buffer, struct ggml_tensor* latents, const float (*latent_rgb_proj)[3], const float latent_rgb_bias[3], int width, int height, int frames, int dim) {
|
||||
void preview_latent_video(uint8_t* buffer, struct ggml_tensor* latents, const float (*latent_rgb_proj)[3], const float latent_rgb_bias[3], int patch_size) {
|
||||
size_t buffer_head = 0;
|
||||
for (int k = 0; k < frames; k++) {
|
||||
for (int j = 0; j < height; j++) {
|
||||
for (int i = 0; i < width; i++) {
|
||||
size_t latent_id = (i * latents->nb[0] + j * latents->nb[1] + k * latents->nb[2]);
|
||||
|
||||
uint32_t latent_width = static_cast<uint32_t>(latents->ne[0]);
|
||||
uint32_t latent_height = static_cast<uint32_t>(latents->ne[1]);
|
||||
uint32_t dim = static_cast<uint32_t>(latents->ne[ggml_n_dims(latents) - 1]);
|
||||
uint32_t frames = 1;
|
||||
if (ggml_n_dims(latents) == 4) {
|
||||
frames = static_cast<uint32_t>(latents->ne[2]);
|
||||
}
|
||||
|
||||
uint32_t rgb_width = latent_width * patch_size;
|
||||
uint32_t rgb_height = latent_height * patch_size;
|
||||
|
||||
uint32_t unpatched_dim = dim / (patch_size * patch_size);
|
||||
|
||||
for (uint32_t k = 0; k < frames; k++) {
|
||||
for (uint32_t rgb_x = 0; rgb_x < rgb_width; rgb_x++) {
|
||||
for (uint32_t rgb_y = 0; rgb_y < rgb_height; rgb_y++) {
|
||||
int latent_x = rgb_x / patch_size;
|
||||
int latent_y = rgb_y / patch_size;
|
||||
|
||||
int channel_offset = 0;
|
||||
if (patch_size > 1) {
|
||||
channel_offset = ((rgb_y % patch_size) * patch_size + (rgb_x % patch_size));
|
||||
}
|
||||
|
||||
size_t latent_id = (latent_x * latents->nb[0] + latent_y * latents->nb[1] + k * latents->nb[2]);
|
||||
|
||||
// should be incremented by 1 for each pixel
|
||||
size_t pixel_id = k * rgb_width * rgb_height + rgb_y * rgb_width + rgb_x;
|
||||
|
||||
float r = 0, g = 0, b = 0;
|
||||
if (latent_rgb_proj != nullptr) {
|
||||
for (int d = 0; d < dim; d++) {
|
||||
float value = *(float*)((char*)latents->data + latent_id + d * latents->nb[ggml_n_dims(latents) - 1]);
|
||||
for (uint32_t d = 0; d < unpatched_dim; d++) {
|
||||
float value = *(float*)((char*)latents->data + latent_id + (d * patch_size * patch_size + channel_offset) * latents->nb[ggml_n_dims(latents) - 1]);
|
||||
r += value * latent_rgb_proj[d][0];
|
||||
g += value * latent_rgb_proj[d][1];
|
||||
b += value * latent_rgb_proj[d][2];
|
||||
@@ -164,9 +225,9 @@ void preview_latent_video(uint8_t* buffer, struct ggml_tensor* latents, const fl
|
||||
g = g >= 0 ? g <= 1 ? g : 1 : 0;
|
||||
b = b >= 0 ? b <= 1 ? b : 1 : 0;
|
||||
|
||||
buffer[buffer_head++] = (uint8_t)(r * 255);
|
||||
buffer[buffer_head++] = (uint8_t)(g * 255);
|
||||
buffer[buffer_head++] = (uint8_t)(b * 255);
|
||||
buffer[pixel_id * 3 + 0] = (uint8_t)(r * 255);
|
||||
buffer[pixel_id * 3 + 1] = (uint8_t)(g * 255);
|
||||
buffer[pixel_id * 3 + 2] = (uint8_t)(b * 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,22 +7,25 @@
|
||||
#define LORA_GRAPH_BASE_SIZE 10240
|
||||
|
||||
struct LoraModel : public GGMLRunner {
|
||||
std::string lora_id;
|
||||
float multiplier = 1.0f;
|
||||
std::map<std::string, struct ggml_tensor*> lora_tensors;
|
||||
std::unordered_map<std::string, struct ggml_tensor*> lora_tensors;
|
||||
std::map<ggml_tensor*, ggml_tensor*> original_tensor_to_final_tensor;
|
||||
std::set<std::string> applied_lora_tensors;
|
||||
std::string file_path;
|
||||
ModelLoader model_loader;
|
||||
bool load_failed = false;
|
||||
bool applied = false;
|
||||
bool tensor_preprocessed = false;
|
||||
std::vector<int> zero_index_vec = {0};
|
||||
ggml_tensor* zero_index = nullptr;
|
||||
bool load_failed = false;
|
||||
bool applied = false;
|
||||
bool tensor_preprocessed = false;
|
||||
|
||||
LoraModel(ggml_backend_t backend,
|
||||
typedef std::function<bool(const std::string&)> filter_t;
|
||||
|
||||
LoraModel(const std::string& lora_id,
|
||||
ggml_backend_t backend,
|
||||
const std::string& file_path = "",
|
||||
std::string prefix = "",
|
||||
SDVersion version = VERSION_COUNT)
|
||||
: file_path(file_path), GGMLRunner(backend, false) {
|
||||
: lora_id(lora_id), file_path(file_path), GGMLRunner(backend, false) {
|
||||
prefix = "lora." + prefix;
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path, prefix, version)) {
|
||||
load_failed = true;
|
||||
@@ -33,7 +36,7 @@ struct LoraModel : public GGMLRunner {
|
||||
return "lora";
|
||||
}
|
||||
|
||||
bool load_from_file(bool filter_tensor, int n_threads) {
|
||||
bool load_from_file(int n_threads, filter_t filter = nullptr) {
|
||||
LOG_INFO("loading LoRA from '%s'", file_path.c_str());
|
||||
|
||||
if (load_failed) {
|
||||
@@ -48,7 +51,7 @@ struct LoraModel : public GGMLRunner {
|
||||
if (dry_run) {
|
||||
const std::string& name = tensor_storage.name;
|
||||
|
||||
if (filter_tensor && !contains(name, "lora.model")) {
|
||||
if (filter && !filter(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -68,6 +71,10 @@ struct LoraModel : public GGMLRunner {
|
||||
|
||||
model_loader.load_tensors(on_new_tensor_cb, n_threads);
|
||||
|
||||
if (tensors_to_create.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& pair : tensors_to_create) {
|
||||
const auto& name = pair.first;
|
||||
const auto& ts = pair.second;
|
||||
@@ -87,14 +94,6 @@ struct LoraModel : public GGMLRunner {
|
||||
return true;
|
||||
}
|
||||
|
||||
ggml_tensor* to_f32(ggml_context* ctx, ggml_tensor* a) {
|
||||
auto out = ggml_reshape_1d(ctx, a, ggml_nelements(a));
|
||||
out = ggml_get_rows(ctx, out, zero_index);
|
||||
out = ggml_reshape(ctx, out, a);
|
||||
// auto out = ggml_cast(ctx, a, GGML_TYPE_F32);
|
||||
return out;
|
||||
}
|
||||
|
||||
void preprocess_lora_tensors(const std::map<std::string, ggml_tensor*>& model_tensors) {
|
||||
if (tensor_preprocessed) {
|
||||
return;
|
||||
@@ -102,7 +101,7 @@ struct LoraModel : public GGMLRunner {
|
||||
tensor_preprocessed = true;
|
||||
// I really hate these hardcoded processes.
|
||||
if (model_tensors.find("cond_stage_model.1.transformer.text_model.encoder.layers.0.self_attn.in_proj.weight") != model_tensors.end()) {
|
||||
std::map<std::string, ggml_tensor*> new_lora_tensors;
|
||||
std::unordered_map<std::string, ggml_tensor*> new_lora_tensors;
|
||||
for (auto& [old_name, tensor] : lora_tensors) {
|
||||
std::string new_name = old_name;
|
||||
|
||||
@@ -130,7 +129,7 @@ struct LoraModel : public GGMLRunner {
|
||||
}
|
||||
}
|
||||
|
||||
ggml_tensor* get_lora_diff(const std::string& model_tensor_name, std::set<std::string>& applied_lora_tensors) {
|
||||
ggml_tensor* get_lora_weight_diff(const std::string& model_tensor_name, ggml_context* ctx) {
|
||||
ggml_tensor* updown = nullptr;
|
||||
int index = 0;
|
||||
while (true) {
|
||||
@@ -153,17 +152,17 @@ struct LoraModel : public GGMLRunner {
|
||||
|
||||
auto iter = lora_tensors.find(lora_up_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lora_up = to_f32(compute_ctx, iter->second);
|
||||
lora_up = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(lora_mid_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lora_mid = to_f32(compute_ctx, iter->second);
|
||||
lora_mid = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(lora_down_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lora_down = to_f32(compute_ctx, iter->second);
|
||||
lora_down = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
if (lora_up == nullptr || lora_down == nullptr) {
|
||||
@@ -195,32 +194,61 @@ struct LoraModel : public GGMLRunner {
|
||||
}
|
||||
scale_value *= multiplier;
|
||||
|
||||
auto curr_updown = ggml_ext_merge_lora(compute_ctx, lora_down, lora_up, lora_mid);
|
||||
curr_updown = ggml_scale_inplace(compute_ctx, curr_updown, scale_value);
|
||||
auto curr_updown = ggml_ext_merge_lora(ctx, lora_down, lora_up, lora_mid);
|
||||
curr_updown = ggml_ext_scale(ctx, curr_updown, scale_value, true);
|
||||
|
||||
if (updown == nullptr) {
|
||||
updown = curr_updown;
|
||||
} else {
|
||||
updown = ggml_concat(compute_ctx, updown, curr_updown, ggml_n_dims(updown) - 1);
|
||||
updown = ggml_concat(ctx, updown, curr_updown, ggml_n_dims(updown) - 1);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
// diff
|
||||
if (updown == nullptr) {
|
||||
std::string lora_diff_name = "lora." + model_tensor_name + ".diff";
|
||||
|
||||
if (lora_tensors.find(lora_diff_name) != lora_tensors.end()) {
|
||||
updown = to_f32(compute_ctx, lora_tensors[lora_diff_name]);
|
||||
applied_lora_tensors.insert(lora_diff_name);
|
||||
}
|
||||
}
|
||||
|
||||
return updown;
|
||||
}
|
||||
|
||||
ggml_tensor* get_loha_diff(const std::string& model_tensor_name, std::set<std::string>& applied_lora_tensors) {
|
||||
ggml_tensor* get_raw_weight_diff(const std::string& model_tensor_name, ggml_context* ctx) {
|
||||
ggml_tensor* updown = nullptr;
|
||||
int index = 0;
|
||||
while (true) {
|
||||
std::string key;
|
||||
if (index == 0) {
|
||||
key = model_tensor_name;
|
||||
} else {
|
||||
key = model_tensor_name + "." + std::to_string(index);
|
||||
}
|
||||
|
||||
std::string diff_name = "lora." + key + ".diff";
|
||||
|
||||
ggml_tensor* curr_updown = nullptr;
|
||||
|
||||
auto iter = lora_tensors.find(diff_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
curr_updown = ggml_ext_cast_f32(ctx, iter->second);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
applied_lora_tensors.insert(diff_name);
|
||||
|
||||
float scale_value = 1.0f;
|
||||
scale_value *= multiplier;
|
||||
|
||||
curr_updown = ggml_ext_scale(ctx, curr_updown, scale_value, true);
|
||||
|
||||
if (updown == nullptr) {
|
||||
updown = curr_updown;
|
||||
} else {
|
||||
updown = ggml_concat(ctx, updown, curr_updown, ggml_n_dims(updown) - 1);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
return updown;
|
||||
}
|
||||
|
||||
ggml_tensor* get_loha_weight_diff(const std::string& model_tensor_name, ggml_context* ctx) {
|
||||
ggml_tensor* updown = nullptr;
|
||||
int index = 0;
|
||||
while (true) {
|
||||
@@ -248,34 +276,34 @@ struct LoraModel : public GGMLRunner {
|
||||
|
||||
auto iter = lora_tensors.find(hada_1_down_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
hada_1_down = to_f32(compute_ctx, iter->second);
|
||||
hada_1_down = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(hada_1_up_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
hada_1_up = to_f32(compute_ctx, iter->second);
|
||||
hada_1_up = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(hada_1_mid_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
hada_1_mid = to_f32(compute_ctx, iter->second);
|
||||
hada_1_up = ggml_cont(compute_ctx, ggml_transpose(compute_ctx, hada_1_up));
|
||||
hada_1_mid = ggml_ext_cast_f32(ctx, iter->second);
|
||||
hada_1_up = ggml_cont(ctx, ggml_transpose(ctx, hada_1_up));
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(hada_2_down_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
hada_2_down = to_f32(compute_ctx, iter->second);
|
||||
hada_2_down = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(hada_2_up_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
hada_2_up = to_f32(compute_ctx, iter->second);
|
||||
hada_2_up = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(hada_2_mid_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
hada_2_mid = to_f32(compute_ctx, iter->second);
|
||||
hada_2_up = ggml_cont(compute_ctx, ggml_transpose(compute_ctx, hada_2_up));
|
||||
hada_2_mid = ggml_ext_cast_f32(ctx, iter->second);
|
||||
hada_2_up = ggml_cont(ctx, ggml_transpose(ctx, hada_2_up));
|
||||
}
|
||||
|
||||
if (hada_1_up == nullptr || hada_1_down == nullptr || hada_2_up == nullptr || hada_2_down == nullptr) {
|
||||
@@ -309,21 +337,21 @@ struct LoraModel : public GGMLRunner {
|
||||
}
|
||||
scale_value *= multiplier;
|
||||
|
||||
struct ggml_tensor* updown_1 = ggml_ext_merge_lora(compute_ctx, hada_1_down, hada_1_up, hada_1_mid);
|
||||
struct ggml_tensor* updown_2 = ggml_ext_merge_lora(compute_ctx, hada_2_down, hada_2_up, hada_2_mid);
|
||||
auto curr_updown = ggml_mul_inplace(compute_ctx, updown_1, updown_2);
|
||||
curr_updown = ggml_scale_inplace(compute_ctx, curr_updown, scale_value);
|
||||
struct ggml_tensor* updown_1 = ggml_ext_merge_lora(ctx, hada_1_down, hada_1_up, hada_1_mid);
|
||||
struct ggml_tensor* updown_2 = ggml_ext_merge_lora(ctx, hada_2_down, hada_2_up, hada_2_mid);
|
||||
auto curr_updown = ggml_mul_inplace(ctx, updown_1, updown_2);
|
||||
curr_updown = ggml_ext_scale(ctx, curr_updown, scale_value, true);
|
||||
if (updown == nullptr) {
|
||||
updown = curr_updown;
|
||||
} else {
|
||||
updown = ggml_concat(compute_ctx, updown, curr_updown, ggml_n_dims(updown) - 1);
|
||||
updown = ggml_concat(ctx, updown, curr_updown, ggml_n_dims(updown) - 1);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return updown;
|
||||
}
|
||||
|
||||
ggml_tensor* get_lokr_diff(const std::string& model_tensor_name, std::set<std::string>& applied_lora_tensors) {
|
||||
ggml_tensor* get_lokr_weight_diff(const std::string& model_tensor_name, ggml_context* ctx) {
|
||||
ggml_tensor* updown = nullptr;
|
||||
int index = 0;
|
||||
while (true) {
|
||||
@@ -350,24 +378,24 @@ struct LoraModel : public GGMLRunner {
|
||||
|
||||
auto iter = lora_tensors.find(lokr_w1_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w1 = to_f32(compute_ctx, iter->second);
|
||||
lokr_w1 = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(lokr_w2_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w2 = to_f32(compute_ctx, iter->second);
|
||||
lokr_w2 = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
int64_t rank = 1;
|
||||
if (lokr_w1 == nullptr) {
|
||||
iter = lora_tensors.find(lokr_w1_a_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w1_a = to_f32(compute_ctx, iter->second);
|
||||
lokr_w1_a = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(lokr_w1_b_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w1_b = to_f32(compute_ctx, iter->second);
|
||||
lokr_w1_b = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
if (lokr_w1_a == nullptr || lokr_w1_b == nullptr) {
|
||||
@@ -376,18 +404,18 @@ struct LoraModel : public GGMLRunner {
|
||||
|
||||
rank = lokr_w1_b->ne[ggml_n_dims(lokr_w1_b) - 1];
|
||||
|
||||
lokr_w1 = ggml_ext_merge_lora(compute_ctx, lokr_w1_b, lokr_w1_a);
|
||||
lokr_w1 = ggml_ext_merge_lora(ctx, lokr_w1_b, lokr_w1_a);
|
||||
}
|
||||
|
||||
if (lokr_w2 == nullptr) {
|
||||
iter = lora_tensors.find(lokr_w2_a_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w2_a = to_f32(compute_ctx, iter->second);
|
||||
lokr_w2_a = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(lokr_w2_b_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w2_b = to_f32(compute_ctx, iter->second);
|
||||
lokr_w2_b = ggml_ext_cast_f32(ctx, iter->second);
|
||||
}
|
||||
|
||||
if (lokr_w2_a == nullptr || lokr_w2_b == nullptr) {
|
||||
@@ -396,7 +424,7 @@ struct LoraModel : public GGMLRunner {
|
||||
|
||||
rank = lokr_w2_b->ne[ggml_n_dims(lokr_w2_b) - 1];
|
||||
|
||||
lokr_w2 = ggml_ext_merge_lora(compute_ctx, lokr_w2_b, lokr_w2_a);
|
||||
lokr_w2 = ggml_ext_merge_lora(ctx, lokr_w2_b, lokr_w2_a);
|
||||
}
|
||||
|
||||
if (!lokr_w1_a) {
|
||||
@@ -427,49 +455,314 @@ struct LoraModel : public GGMLRunner {
|
||||
|
||||
scale_value *= multiplier;
|
||||
|
||||
auto curr_updown = ggml_ext_kronecker(compute_ctx, lokr_w1, lokr_w2);
|
||||
curr_updown = ggml_scale_inplace(compute_ctx, curr_updown, scale_value);
|
||||
auto curr_updown = ggml_ext_kronecker(ctx, lokr_w1, lokr_w2);
|
||||
curr_updown = ggml_ext_scale(ctx, curr_updown, scale_value, true);
|
||||
|
||||
if (updown == nullptr) {
|
||||
updown = curr_updown;
|
||||
} else {
|
||||
updown = ggml_concat(compute_ctx, updown, curr_updown, ggml_n_dims(updown) - 1);
|
||||
updown = ggml_concat(ctx, updown, curr_updown, ggml_n_dims(updown) - 1);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return updown;
|
||||
}
|
||||
|
||||
ggml_tensor* get_weight_diff(const std::string& model_tensor_name, ggml_context* ctx, ggml_tensor* model_tensor, bool with_lora_and_lokr = true) {
|
||||
// lora
|
||||
ggml_tensor* diff = nullptr;
|
||||
if (with_lora_and_lokr) {
|
||||
diff = get_lora_weight_diff(model_tensor_name, ctx);
|
||||
}
|
||||
// diff
|
||||
if (diff == nullptr) {
|
||||
diff = get_raw_weight_diff(model_tensor_name, ctx);
|
||||
}
|
||||
// loha
|
||||
if (diff == nullptr) {
|
||||
diff = get_loha_weight_diff(model_tensor_name, ctx);
|
||||
}
|
||||
// lokr
|
||||
if (diff == nullptr && with_lora_and_lokr) {
|
||||
diff = get_lokr_weight_diff(model_tensor_name, ctx);
|
||||
}
|
||||
if (diff != nullptr) {
|
||||
if (ggml_nelements(diff) < ggml_nelements(model_tensor)) {
|
||||
if (ggml_n_dims(diff) == 2 && ggml_n_dims(model_tensor) == 2 && diff->ne[0] == model_tensor->ne[0]) {
|
||||
LOG_WARN("pad for %s", model_tensor_name.c_str());
|
||||
auto pad_tensor = ggml_ext_zeros(ctx, diff->ne[0], model_tensor->ne[1] - diff->ne[1], 1, 1);
|
||||
diff = ggml_concat(ctx, diff, pad_tensor, 1);
|
||||
}
|
||||
}
|
||||
|
||||
GGML_ASSERT(ggml_nelements(diff) == ggml_nelements(model_tensor));
|
||||
diff = ggml_reshape(ctx, diff, model_tensor);
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
ggml_tensor* get_out_diff(ggml_context* ctx,
|
||||
ggml_tensor* x,
|
||||
WeightAdapter::ForwardParams forward_params,
|
||||
const std::string& model_tensor_name) {
|
||||
ggml_tensor* out_diff = nullptr;
|
||||
int index = 0;
|
||||
while (true) {
|
||||
std::string key;
|
||||
if (index == 0) {
|
||||
key = model_tensor_name;
|
||||
} else {
|
||||
key = model_tensor_name + "." + std::to_string(index);
|
||||
}
|
||||
bool is_conv2d = forward_params.op_type == WeightAdapter::ForwardParams::op_type_t::OP_CONV2D;
|
||||
|
||||
std::string lokr_w1_name = "lora." + key + ".lokr_w1";
|
||||
std::string lokr_w1_a_name = "lora." + key + ".lokr_w1_a";
|
||||
// if either of these is found, then we have a lokr lora
|
||||
auto iter = lora_tensors.find(lokr_w1_name);
|
||||
auto iter_a = lora_tensors.find(lokr_w1_a_name);
|
||||
if (iter != lora_tensors.end() || iter_a != lora_tensors.end()) {
|
||||
std::string lokr_w1_b_name = "lora." + key + ".lokr_w1_b";
|
||||
std::string lokr_w2_name = "lora." + key + ".lokr_w2";
|
||||
std::string lokr_w2_a_name = "lora." + key + ".lokr_w2_a";
|
||||
std::string lokr_w2_b_name = "lora." + key + ".lokr_w2_b";
|
||||
std::string alpha_name = "lora." + key + ".alpha";
|
||||
|
||||
ggml_tensor* lokr_w1 = nullptr;
|
||||
ggml_tensor* lokr_w1_a = nullptr;
|
||||
ggml_tensor* lokr_w1_b = nullptr;
|
||||
ggml_tensor* lokr_w2 = nullptr;
|
||||
ggml_tensor* lokr_w2_a = nullptr;
|
||||
ggml_tensor* lokr_w2_b = nullptr;
|
||||
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w1 = iter->second;
|
||||
}
|
||||
iter = iter_a;
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w1_a = iter->second;
|
||||
}
|
||||
iter = lora_tensors.find(lokr_w1_b_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w1_b = iter->second;
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(lokr_w2_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w2 = iter->second;
|
||||
if (is_conv2d && lokr_w2->type != GGML_TYPE_F16) {
|
||||
lokr_w2 = ggml_cast(ctx, lokr_w2, GGML_TYPE_F16);
|
||||
}
|
||||
}
|
||||
iter = lora_tensors.find(lokr_w2_a_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w2_a = iter->second;
|
||||
if (is_conv2d && lokr_w2_a->type != GGML_TYPE_F16) {
|
||||
lokr_w2_a = ggml_cast(ctx, lokr_w2_a, GGML_TYPE_F16);
|
||||
}
|
||||
}
|
||||
iter = lora_tensors.find(lokr_w2_b_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lokr_w2_b = iter->second;
|
||||
if (is_conv2d && lokr_w2_b->type != GGML_TYPE_F16) {
|
||||
lokr_w2_b = ggml_cast(ctx, lokr_w2_b, GGML_TYPE_F16);
|
||||
}
|
||||
}
|
||||
|
||||
int rank = 1;
|
||||
if (lokr_w1_b) {
|
||||
rank = (int)lokr_w1_b->ne[ggml_n_dims(lokr_w1_b) - 1];
|
||||
}
|
||||
if (lokr_w2_b) {
|
||||
rank = (int)lokr_w2_b->ne[ggml_n_dims(lokr_w2_b) - 1];
|
||||
}
|
||||
|
||||
float scale_value = 1.0f;
|
||||
iter = lora_tensors.find(alpha_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
float alpha = ggml_ext_backend_tensor_get_f32(iter->second);
|
||||
scale_value = alpha / rank;
|
||||
applied_lora_tensors.insert(alpha_name);
|
||||
}
|
||||
|
||||
if (rank == 1) {
|
||||
scale_value = 1.0f;
|
||||
}
|
||||
scale_value *= multiplier;
|
||||
|
||||
auto curr_out_diff = ggml_ext_lokr_forward(ctx, x, lokr_w1, lokr_w1_a, lokr_w1_b, lokr_w2, lokr_w2_a, lokr_w2_b, is_conv2d, forward_params.conv2d, scale_value);
|
||||
if (out_diff == nullptr) {
|
||||
out_diff = curr_out_diff;
|
||||
} else {
|
||||
out_diff = ggml_concat(ctx, out_diff, curr_out_diff, 0);
|
||||
}
|
||||
|
||||
if (lokr_w1)
|
||||
applied_lora_tensors.insert(lokr_w1_name);
|
||||
if (lokr_w1_a)
|
||||
applied_lora_tensors.insert(lokr_w1_a_name);
|
||||
if (lokr_w1_b)
|
||||
applied_lora_tensors.insert(lokr_w1_b_name);
|
||||
if (lokr_w2)
|
||||
applied_lora_tensors.insert(lokr_w2_name);
|
||||
if (lokr_w2_a)
|
||||
applied_lora_tensors.insert(lokr_w2_name);
|
||||
if (lokr_w2_b)
|
||||
applied_lora_tensors.insert(lokr_w2_b_name);
|
||||
applied_lora_tensors.insert(alpha_name);
|
||||
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// not a lokr, normal lora path
|
||||
|
||||
std::string lora_down_name = "lora." + key + ".lora_down";
|
||||
std::string lora_up_name = "lora." + key + ".lora_up";
|
||||
std::string lora_mid_name = "lora." + key + ".lora_mid";
|
||||
std::string scale_name = "lora." + key + ".scale";
|
||||
std::string alpha_name = "lora." + key + ".alpha";
|
||||
|
||||
ggml_tensor* lora_up = nullptr;
|
||||
ggml_tensor* lora_mid = nullptr;
|
||||
ggml_tensor* lora_down = nullptr;
|
||||
|
||||
iter = lora_tensors.find(lora_up_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lora_up = iter->second;
|
||||
if (is_conv2d && lora_up->type != GGML_TYPE_F16) {
|
||||
lora_up = ggml_cast(ctx, lora_up, GGML_TYPE_F16);
|
||||
}
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(lora_mid_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lora_mid = iter->second;
|
||||
if (is_conv2d && lora_mid->type != GGML_TYPE_F16) {
|
||||
lora_mid = ggml_cast(ctx, lora_mid, GGML_TYPE_F16);
|
||||
}
|
||||
}
|
||||
|
||||
iter = lora_tensors.find(lora_down_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
lora_down = iter->second;
|
||||
if (is_conv2d && lora_down->type != GGML_TYPE_F16) {
|
||||
lora_down = ggml_cast(ctx, lora_down, GGML_TYPE_F16);
|
||||
}
|
||||
}
|
||||
|
||||
if (lora_up == nullptr || lora_down == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
applied_lora_tensors.insert(lora_up_name);
|
||||
applied_lora_tensors.insert(lora_down_name);
|
||||
|
||||
if (lora_mid) {
|
||||
applied_lora_tensors.insert(lora_mid_name);
|
||||
}
|
||||
|
||||
float scale_value = 1.0f;
|
||||
|
||||
int64_t rank = lora_down->ne[ggml_n_dims(lora_down) - 1];
|
||||
iter = lora_tensors.find(scale_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
scale_value = ggml_ext_backend_tensor_get_f32(iter->second);
|
||||
applied_lora_tensors.insert(scale_name);
|
||||
} else {
|
||||
iter = lora_tensors.find(alpha_name);
|
||||
if (iter != lora_tensors.end()) {
|
||||
float alpha = ggml_ext_backend_tensor_get_f32(iter->second);
|
||||
scale_value = alpha / rank;
|
||||
// LOG_DEBUG("rank %s %ld %.2f %.2f", alpha_name.c_str(), rank, alpha, scale_value);
|
||||
applied_lora_tensors.insert(alpha_name);
|
||||
}
|
||||
}
|
||||
scale_value *= multiplier;
|
||||
|
||||
ggml_tensor* lx;
|
||||
if (!is_conv2d) {
|
||||
lx = ggml_ext_linear(ctx, x, lora_down, nullptr, forward_params.linear.force_prec_f32, forward_params.linear.scale);
|
||||
if (lora_mid) {
|
||||
lx = ggml_ext_linear(ctx, lx, lora_mid, nullptr, forward_params.linear.force_prec_f32, forward_params.linear.scale);
|
||||
}
|
||||
lx = ggml_ext_linear(ctx, lx, lora_up, nullptr, forward_params.linear.force_prec_f32, forward_params.linear.scale);
|
||||
} else { // OP_CONV2D
|
||||
lx = ggml_ext_conv_2d(ctx,
|
||||
x,
|
||||
lora_down,
|
||||
nullptr,
|
||||
forward_params.conv2d.s0,
|
||||
forward_params.conv2d.s1,
|
||||
forward_params.conv2d.p0,
|
||||
forward_params.conv2d.p1,
|
||||
forward_params.conv2d.d0,
|
||||
forward_params.conv2d.d1,
|
||||
forward_params.conv2d.direct,
|
||||
forward_params.conv2d.circular_x,
|
||||
forward_params.conv2d.circular_y,
|
||||
forward_params.conv2d.scale);
|
||||
if (lora_mid) {
|
||||
lx = ggml_ext_conv_2d(ctx,
|
||||
lx,
|
||||
lora_mid,
|
||||
nullptr,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
forward_params.conv2d.direct,
|
||||
forward_params.conv2d.circular_x,
|
||||
forward_params.conv2d.circular_y,
|
||||
forward_params.conv2d.scale);
|
||||
}
|
||||
lx = ggml_ext_conv_2d(ctx,
|
||||
lx,
|
||||
lora_up,
|
||||
nullptr,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
forward_params.conv2d.direct,
|
||||
forward_params.conv2d.circular_x,
|
||||
forward_params.conv2d.circular_y,
|
||||
forward_params.conv2d.scale);
|
||||
}
|
||||
|
||||
auto curr_out_diff = ggml_ext_scale(ctx, lx, scale_value, true);
|
||||
|
||||
if (out_diff == nullptr) {
|
||||
out_diff = curr_out_diff;
|
||||
} else {
|
||||
out_diff = ggml_concat(ctx, out_diff, curr_out_diff, 0);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
return out_diff;
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_lora_graph(const std::map<std::string, ggml_tensor*>& model_tensors, SDVersion version) {
|
||||
size_t lora_graph_size = LORA_GRAPH_BASE_SIZE + lora_tensors.size() * 10;
|
||||
struct ggml_cgraph* gf = ggml_new_graph_custom(compute_ctx, lora_graph_size, false);
|
||||
|
||||
zero_index = ggml_new_tensor_1d(compute_ctx, GGML_TYPE_I32, 1);
|
||||
set_backend_tensor_data(zero_index, zero_index_vec.data());
|
||||
ggml_build_forward_expand(gf, zero_index);
|
||||
|
||||
preprocess_lora_tensors(model_tensors);
|
||||
|
||||
original_tensor_to_final_tensor.clear();
|
||||
applied_lora_tensors.clear();
|
||||
|
||||
std::set<std::string> applied_lora_tensors;
|
||||
for (auto it : model_tensors) {
|
||||
std::string model_tensor_name = it.first;
|
||||
ggml_tensor* model_tensor = it.second;
|
||||
|
||||
// lora
|
||||
ggml_tensor* updown = get_lora_diff(model_tensor_name, applied_lora_tensors);
|
||||
// loha
|
||||
if (updown == nullptr) {
|
||||
updown = get_loha_diff(model_tensor_name, applied_lora_tensors);
|
||||
}
|
||||
|
||||
// lokr
|
||||
if (updown == nullptr) {
|
||||
updown = get_lokr_diff(model_tensor_name, applied_lora_tensors);
|
||||
}
|
||||
|
||||
if (updown == nullptr) {
|
||||
ggml_tensor* diff = get_weight_diff(model_tensor_name, compute_ctx, model_tensor);
|
||||
if (diff == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -479,53 +772,19 @@ struct LoraModel : public GGMLRunner {
|
||||
set_backend_tensor_data(model_tensor, original_tensor->data);
|
||||
}
|
||||
|
||||
if (ggml_nelements(updown) < ggml_nelements(model_tensor)) {
|
||||
if (ggml_n_dims(updown) == 2 && ggml_n_dims(model_tensor) == 2 && updown->ne[0] == model_tensor->ne[0]) {
|
||||
LOG_WARN("pad for %s", model_tensor_name.c_str());
|
||||
auto pad_tensor = ggml_ext_zeros(compute_ctx, updown->ne[0], model_tensor->ne[1] - updown->ne[1], 1, 1);
|
||||
updown = ggml_concat(compute_ctx, updown, pad_tensor, 1);
|
||||
}
|
||||
}
|
||||
|
||||
GGML_ASSERT(ggml_nelements(updown) == ggml_nelements(model_tensor));
|
||||
updown = ggml_reshape(compute_ctx, updown, model_tensor);
|
||||
ggml_tensor* final_tensor;
|
||||
if (model_tensor->type != GGML_TYPE_F32 && model_tensor->type != GGML_TYPE_F16) {
|
||||
final_tensor = to_f32(compute_ctx, model_tensor);
|
||||
final_tensor = ggml_add_inplace(compute_ctx, final_tensor, updown);
|
||||
final_tensor = ggml_ext_cast_f32(compute_ctx, model_tensor);
|
||||
final_tensor = ggml_add_inplace(compute_ctx, final_tensor, diff);
|
||||
final_tensor = ggml_cpy(compute_ctx, final_tensor, model_tensor);
|
||||
} else {
|
||||
final_tensor = ggml_add_inplace(compute_ctx, model_tensor, updown);
|
||||
final_tensor = ggml_add_inplace(compute_ctx, model_tensor, diff);
|
||||
}
|
||||
ggml_build_forward_expand(gf, final_tensor);
|
||||
if (!ggml_backend_is_cpu(runtime_backend) && ggml_backend_buffer_is_host(original_tensor->buffer)) {
|
||||
original_tensor_to_final_tensor[original_tensor] = final_tensor;
|
||||
}
|
||||
}
|
||||
size_t total_lora_tensors_count = 0;
|
||||
size_t applied_lora_tensors_count = 0;
|
||||
|
||||
for (auto& kv : lora_tensors) {
|
||||
total_lora_tensors_count++;
|
||||
if (applied_lora_tensors.find(kv.first) == applied_lora_tensors.end()) {
|
||||
LOG_WARN("unused lora tensor |%s|", kv.first.c_str());
|
||||
print_ggml_tensor(kv.second, true);
|
||||
// exit(0);
|
||||
} else {
|
||||
applied_lora_tensors_count++;
|
||||
}
|
||||
}
|
||||
/* Don't worry if this message shows up twice in the logs per LoRA,
|
||||
* this function is called once to calculate the required buffer size
|
||||
* and then again to actually generate a graph to be used */
|
||||
if (applied_lora_tensors_count != total_lora_tensors_count) {
|
||||
LOG_WARN("Only (%lu / %lu) LoRA tensors will be applied",
|
||||
applied_lora_tensors_count, total_lora_tensors_count);
|
||||
} else {
|
||||
LOG_DEBUG("(%lu / %lu) LoRA tensors will be applied",
|
||||
applied_lora_tensors_count, total_lora_tensors_count);
|
||||
}
|
||||
|
||||
return gf;
|
||||
}
|
||||
|
||||
@@ -534,6 +793,7 @@ struct LoraModel : public GGMLRunner {
|
||||
return build_lora_graph(model_tensors, version);
|
||||
};
|
||||
GGMLRunner::compute(get_graph, n_threads, false);
|
||||
stat();
|
||||
for (auto item : original_tensor_to_final_tensor) {
|
||||
ggml_tensor* original_tensor = item.first;
|
||||
ggml_tensor* final_tensor = item.second;
|
||||
@@ -543,6 +803,109 @@ struct LoraModel : public GGMLRunner {
|
||||
original_tensor_to_final_tensor.clear();
|
||||
GGMLRunner::free_compute_buffer();
|
||||
}
|
||||
|
||||
void stat(bool at_runntime = false) {
|
||||
size_t total_lora_tensors_count = 0;
|
||||
size_t applied_lora_tensors_count = 0;
|
||||
|
||||
for (auto& kv : lora_tensors) {
|
||||
total_lora_tensors_count++;
|
||||
if (applied_lora_tensors.find(kv.first) == applied_lora_tensors.end()) {
|
||||
if (!at_runntime) {
|
||||
LOG_WARN("unused lora tensor |%s|", kv.first.c_str());
|
||||
print_ggml_tensor(kv.second, true);
|
||||
}
|
||||
} else {
|
||||
applied_lora_tensors_count++;
|
||||
}
|
||||
}
|
||||
/* Don't worry if this message shows up twice in the logs per LoRA,
|
||||
* this function is called once to calculate the required buffer size
|
||||
* and then again to actually generate a graph to be used */
|
||||
if (!at_runntime && applied_lora_tensors_count != total_lora_tensors_count) {
|
||||
LOG_WARN("Only (%lu / %lu) LoRA tensors have been applied, lora_file_path = %s",
|
||||
applied_lora_tensors_count, total_lora_tensors_count, file_path.c_str());
|
||||
} else {
|
||||
LOG_INFO("(%lu / %lu) LoRA tensors have been applied, lora_file_path = %s",
|
||||
applied_lora_tensors_count, total_lora_tensors_count, file_path.c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct MultiLoraAdapter : public WeightAdapter {
|
||||
protected:
|
||||
std::vector<std::shared_ptr<LoraModel>> lora_models;
|
||||
|
||||
public:
|
||||
explicit MultiLoraAdapter(const std::vector<std::shared_ptr<LoraModel>>& lora_models)
|
||||
: lora_models(lora_models) {
|
||||
}
|
||||
|
||||
ggml_tensor* patch_weight(ggml_context* ctx, ggml_tensor* weight, const std::string& weight_name, bool with_lora_and_lokr) {
|
||||
for (auto& lora_model : lora_models) {
|
||||
ggml_tensor* diff = lora_model->get_weight_diff(weight_name, ctx, weight, with_lora_and_lokr);
|
||||
if (diff == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (weight->type != GGML_TYPE_F32 && weight->type != GGML_TYPE_F16) {
|
||||
weight = ggml_ext_cast_f32(ctx, weight);
|
||||
}
|
||||
weight = ggml_add(ctx, weight, diff);
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
ggml_tensor* patch_weight(ggml_context* ctx, ggml_tensor* weight, const std::string& weight_name) override {
|
||||
return patch_weight(ctx, weight, weight_name, true);
|
||||
}
|
||||
|
||||
ggml_tensor* forward_with_lora(ggml_context* ctx,
|
||||
ggml_tensor* x,
|
||||
ggml_tensor* w,
|
||||
ggml_tensor* b,
|
||||
const std::string& prefix,
|
||||
WeightAdapter::ForwardParams forward_params) override {
|
||||
w = patch_weight(ctx, w, prefix + "weight", false);
|
||||
if (b) {
|
||||
b = patch_weight(ctx, b, prefix + "bias", false);
|
||||
}
|
||||
ggml_tensor* out;
|
||||
if (forward_params.op_type == ForwardParams::op_type_t::OP_LINEAR) {
|
||||
out = ggml_ext_linear(ctx, x, w, b, forward_params.linear.force_prec_f32, forward_params.linear.scale);
|
||||
} else { // OP_CONV2D
|
||||
out = ggml_ext_conv_2d(ctx,
|
||||
x,
|
||||
w,
|
||||
b,
|
||||
forward_params.conv2d.s0,
|
||||
forward_params.conv2d.s1,
|
||||
forward_params.conv2d.p0,
|
||||
forward_params.conv2d.p1,
|
||||
forward_params.conv2d.d0,
|
||||
forward_params.conv2d.d1,
|
||||
forward_params.conv2d.direct,
|
||||
forward_params.conv2d.circular_x,
|
||||
forward_params.conv2d.circular_y,
|
||||
forward_params.conv2d.scale);
|
||||
}
|
||||
for (auto& lora_model : lora_models) {
|
||||
ggml_tensor* out_diff = lora_model->get_out_diff(ctx, x, forward_params, prefix + "weight");
|
||||
if (out_diff == nullptr) {
|
||||
continue;
|
||||
}
|
||||
out = ggml_add_inplace(ctx, out, out_diff);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t get_extra_graph_size() override {
|
||||
size_t lora_tensor_num = 0;
|
||||
for (auto& lora_model : lora_models) {
|
||||
lora_tensor_num += lora_model->lora_tensors.size();
|
||||
}
|
||||
return LORA_GRAPH_BASE_SIZE + lora_tensor_num * 10;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __LORA_HPP__
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
auto fc2 = std::dynamic_pointer_cast<Linear>(blocks["fc2"]);
|
||||
|
||||
x = fc1->forward(ctx, x);
|
||||
x = ggml_gelu_inplace(ctx->ggml_ctx, x);
|
||||
x = ggml_ext_gelu(ctx->ggml_ctx, x, true);
|
||||
x = fc2->forward(ctx, x);
|
||||
return x;
|
||||
}
|
||||
@@ -97,14 +97,18 @@ public:
|
||||
struct TimestepEmbedder : public GGMLBlock {
|
||||
// Embeds scalar timesteps into vector representations.
|
||||
protected:
|
||||
int64_t frequency_embedding_size;
|
||||
int frequency_embedding_size;
|
||||
|
||||
public:
|
||||
TimestepEmbedder(int64_t hidden_size,
|
||||
int64_t frequency_embedding_size = 256)
|
||||
int frequency_embedding_size = 256,
|
||||
int64_t out_channels = 0)
|
||||
: frequency_embedding_size(frequency_embedding_size) {
|
||||
if (out_channels <= 0) {
|
||||
out_channels = hidden_size;
|
||||
}
|
||||
blocks["mlp.0"] = std::shared_ptr<GGMLBlock>(new Linear(frequency_embedding_size, hidden_size, true, true));
|
||||
blocks["mlp.2"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size, hidden_size, true, true));
|
||||
blocks["mlp.2"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size, out_channels, true, true));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* t) {
|
||||
@@ -163,11 +167,11 @@ public:
|
||||
blocks["proj"] = std::shared_ptr<GGMLBlock>(new Linear(dim, dim));
|
||||
}
|
||||
if (qk_norm == "rms") {
|
||||
blocks["ln_q"] = std::shared_ptr<GGMLBlock>(new RMSNorm(d_head, 1.0e-6));
|
||||
blocks["ln_k"] = std::shared_ptr<GGMLBlock>(new RMSNorm(d_head, 1.0e-6));
|
||||
blocks["ln_q"] = std::shared_ptr<GGMLBlock>(new RMSNorm(d_head, 1.0e-6f));
|
||||
blocks["ln_k"] = std::shared_ptr<GGMLBlock>(new RMSNorm(d_head, 1.0e-6f));
|
||||
} else if (qk_norm == "ln") {
|
||||
blocks["ln_q"] = std::shared_ptr<GGMLBlock>(new LayerNorm(d_head, 1.0e-6));
|
||||
blocks["ln_k"] = std::shared_ptr<GGMLBlock>(new LayerNorm(d_head, 1.0e-6));
|
||||
blocks["ln_q"] = std::shared_ptr<GGMLBlock>(new LayerNorm(d_head, 1.0e-6f));
|
||||
blocks["ln_k"] = std::shared_ptr<GGMLBlock>(new LayerNorm(d_head, 1.0e-6f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,8 +211,8 @@ public:
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
struct ggml_tensor* x) {
|
||||
auto qkv = pre_attention(ctx, x);
|
||||
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, false, ctx->flash_attn_enabled); // [N, n_token, dim]
|
||||
x = post_attention(ctx, x); // [N, n_token, dim]
|
||||
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
|
||||
x = post_attention(ctx, x); // [N, n_token, dim]
|
||||
return x;
|
||||
}
|
||||
};
|
||||
@@ -280,23 +284,19 @@ public:
|
||||
auto attn2 = std::dynamic_pointer_cast<SelfAttention>(blocks["attn2"]);
|
||||
auto adaLN_modulation_1 = std::dynamic_pointer_cast<Linear>(blocks["adaLN_modulation.1"]);
|
||||
|
||||
int64_t n_mods = 9;
|
||||
auto m = adaLN_modulation_1->forward(ctx, ggml_silu(ctx->ggml_ctx, c)); // [N, n_mods * hidden_size]
|
||||
m = ggml_reshape_3d(ctx->ggml_ctx, m, c->ne[0], n_mods, c->ne[1]); // [N, n_mods, hidden_size]
|
||||
m = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, m, 0, 2, 1, 3)); // [n_mods, N, hidden_size]
|
||||
int n_mods = 9;
|
||||
auto m = adaLN_modulation_1->forward(ctx, ggml_silu(ctx->ggml_ctx, c)); // [N, n_mods * hidden_size]
|
||||
auto m_vec = ggml_ext_chunk(ctx->ggml_ctx, m, n_mods, 0);
|
||||
|
||||
int64_t offset = m->nb[1] * m->ne[1];
|
||||
auto shift_msa = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 0); // [N, hidden_size]
|
||||
auto scale_msa = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 1); // [N, hidden_size]
|
||||
auto gate_msa = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 2); // [N, hidden_size]
|
||||
|
||||
auto shift_mlp = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 3); // [N, hidden_size]
|
||||
auto scale_mlp = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 4); // [N, hidden_size]
|
||||
auto gate_mlp = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 5); // [N, hidden_size]
|
||||
|
||||
auto shift_msa2 = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 6); // [N, hidden_size]
|
||||
auto scale_msa2 = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 7); // [N, hidden_size]
|
||||
auto gate_msa2 = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 8); // [N, hidden_size]
|
||||
auto shift_msa = m_vec[0]; // [N, hidden_size]
|
||||
auto scale_msa = m_vec[1]; // [N, hidden_size]
|
||||
auto gate_msa = m_vec[2]; // [N, hidden_size]
|
||||
auto shift_mlp = m_vec[3]; // [N, hidden_size]
|
||||
auto scale_mlp = m_vec[4]; // [N, hidden_size]
|
||||
auto gate_mlp = m_vec[5]; // [N, hidden_size]
|
||||
auto shift_msa2 = m_vec[6]; // [N, hidden_size]
|
||||
auto scale_msa2 = m_vec[7]; // [N, hidden_size]
|
||||
auto gate_msa2 = m_vec[8]; // [N, hidden_size]
|
||||
|
||||
auto x_norm = norm1->forward(ctx, x);
|
||||
|
||||
@@ -318,22 +318,20 @@ public:
|
||||
auto attn = std::dynamic_pointer_cast<SelfAttention>(blocks["attn"]);
|
||||
auto adaLN_modulation_1 = std::dynamic_pointer_cast<Linear>(blocks["adaLN_modulation.1"]);
|
||||
|
||||
int64_t n_mods = 6;
|
||||
int n_mods = 6;
|
||||
if (pre_only) {
|
||||
n_mods = 2;
|
||||
}
|
||||
auto m = adaLN_modulation_1->forward(ctx, ggml_silu(ctx->ggml_ctx, c)); // [N, n_mods * hidden_size]
|
||||
m = ggml_reshape_3d(ctx->ggml_ctx, m, c->ne[0], n_mods, c->ne[1]); // [N, n_mods, hidden_size]
|
||||
m = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, m, 0, 2, 1, 3)); // [n_mods, N, hidden_size]
|
||||
auto m = adaLN_modulation_1->forward(ctx, ggml_silu(ctx->ggml_ctx, c)); // [N, n_mods * hidden_size]
|
||||
auto m_vec = ggml_ext_chunk(ctx->ggml_ctx, m, n_mods, 0);
|
||||
|
||||
int64_t offset = m->nb[1] * m->ne[1];
|
||||
auto shift_msa = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 0); // [N, hidden_size]
|
||||
auto scale_msa = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 1); // [N, hidden_size]
|
||||
auto shift_msa = m_vec[0]; // [N, hidden_size]
|
||||
auto scale_msa = m_vec[1]; // [N, hidden_size]
|
||||
if (!pre_only) {
|
||||
auto gate_msa = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 2); // [N, hidden_size]
|
||||
auto shift_mlp = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 3); // [N, hidden_size]
|
||||
auto scale_mlp = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 4); // [N, hidden_size]
|
||||
auto gate_mlp = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 5); // [N, hidden_size]
|
||||
auto gate_msa = m_vec[2]; // [N, hidden_size]
|
||||
auto shift_mlp = m_vec[3]; // [N, hidden_size]
|
||||
auto scale_mlp = m_vec[4]; // [N, hidden_size]
|
||||
auto gate_mlp = m_vec[5]; // [N, hidden_size]
|
||||
|
||||
auto attn_in = modulate(ctx->ggml_ctx, norm1->forward(ctx, x), shift_msa, scale_msa);
|
||||
|
||||
@@ -435,8 +433,8 @@ public:
|
||||
auto qkv2 = std::get<1>(qkv_intermediates);
|
||||
auto intermediates = std::get<2>(qkv_intermediates);
|
||||
|
||||
auto attn_out = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, false, ctx->flash_attn_enabled); // [N, n_token, dim]
|
||||
auto attn2_out = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv2[0], qkv2[1], qkv2[2], num_heads, nullptr, false, false, ctx->flash_attn_enabled); // [N, n_token, dim]
|
||||
auto attn_out = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
|
||||
auto attn2_out = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv2[0], qkv2[1], qkv2[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
|
||||
x = post_attention_x(ctx,
|
||||
attn_out,
|
||||
attn2_out,
|
||||
@@ -452,7 +450,7 @@ public:
|
||||
auto qkv = qkv_intermediates.first;
|
||||
auto intermediates = qkv_intermediates.second;
|
||||
|
||||
auto attn_out = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, false, ctx->flash_attn_enabled); // [N, n_token, dim]
|
||||
auto attn_out = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
|
||||
x = post_attention(ctx,
|
||||
attn_out,
|
||||
intermediates[0],
|
||||
@@ -496,26 +494,24 @@ block_mixing(GGMLRunnerContext* ctx,
|
||||
qkv.push_back(ggml_concat(ctx->ggml_ctx, context_qkv[i], x_qkv[i], 1));
|
||||
}
|
||||
|
||||
auto attn = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], x_block->num_heads, nullptr, false, false, ctx->flash_attn_enabled); // [N, n_context + n_token, hidden_size]
|
||||
attn = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, attn, 0, 2, 1, 3)); // [n_context + n_token, N, hidden_size]
|
||||
auto attn = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], x_block->num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_context + n_token, hidden_size]
|
||||
|
||||
auto context_attn = ggml_view_3d(ctx->ggml_ctx,
|
||||
attn,
|
||||
attn->ne[0],
|
||||
attn->ne[1],
|
||||
context->ne[1],
|
||||
attn->ne[2],
|
||||
attn->nb[1],
|
||||
attn->nb[2],
|
||||
0); // [n_context, N, hidden_size]
|
||||
context_attn = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, context_attn, 0, 2, 1, 3)); // [N, n_context, hidden_size]
|
||||
0); // [N, n_context, hidden_size]
|
||||
auto x_attn = ggml_view_3d(ctx->ggml_ctx,
|
||||
attn,
|
||||
attn->ne[0],
|
||||
attn->ne[1],
|
||||
x->ne[1],
|
||||
attn->ne[2],
|
||||
attn->nb[1],
|
||||
attn->nb[2],
|
||||
attn->nb[2] * context->ne[1]); // [n_token, N, hidden_size]
|
||||
x_attn = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x_attn, 0, 2, 1, 3)); // [N, n_token, hidden_size]
|
||||
context->ne[1] * attn->nb[1]); // [N, n_token, hidden_size]
|
||||
|
||||
if (!context_block->pre_only) {
|
||||
context = context_block->post_attention(ctx,
|
||||
@@ -530,7 +526,7 @@ block_mixing(GGMLRunnerContext* ctx,
|
||||
}
|
||||
|
||||
if (x_block->self_attn) {
|
||||
auto attn2 = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, x_qkv2[0], x_qkv2[1], x_qkv2[2], x_block->num_heads, nullptr, false, false, ctx->flash_attn_enabled); // [N, n_token, hidden_size]
|
||||
auto attn2 = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, x_qkv2[0], x_qkv2[1], x_qkv2[2], x_block->num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, hidden_size]
|
||||
|
||||
x = x_block->post_attention_x(ctx,
|
||||
x_attn,
|
||||
@@ -600,13 +596,10 @@ public:
|
||||
auto linear = std::dynamic_pointer_cast<Linear>(blocks["linear"]);
|
||||
auto adaLN_modulation_1 = std::dynamic_pointer_cast<Linear>(blocks["adaLN_modulation.1"]);
|
||||
|
||||
auto m = adaLN_modulation_1->forward(ctx, ggml_silu(ctx->ggml_ctx, c)); // [N, 2 * hidden_size]
|
||||
m = ggml_reshape_3d(ctx->ggml_ctx, m, c->ne[0], 2, c->ne[1]); // [N, 2, hidden_size]
|
||||
m = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, m, 0, 2, 1, 3)); // [2, N, hidden_size]
|
||||
|
||||
int64_t offset = m->nb[1] * m->ne[1];
|
||||
auto shift = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 0); // [N, hidden_size]
|
||||
auto scale = ggml_view_2d(ctx->ggml_ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 1); // [N, hidden_size]
|
||||
auto m = adaLN_modulation_1->forward(ctx, ggml_silu(ctx->ggml_ctx, c)); // [N, 2 * hidden_size]
|
||||
auto m_vec = ggml_ext_chunk(ctx->ggml_ctx, m, 2, 0);
|
||||
auto shift = m_vec[0]; // [N, hidden_size]
|
||||
auto scale = m_vec[1]; // [N, hidden_size]
|
||||
|
||||
x = modulate(ctx->ggml_ctx, norm_final->forward(ctx, x), shift, scale);
|
||||
x = linear->forward(ctx, x);
|
||||
@@ -619,7 +612,7 @@ struct MMDiT : public GGMLBlock {
|
||||
// Diffusion model with a Transformer backbone.
|
||||
protected:
|
||||
int64_t input_size = -1;
|
||||
int64_t patch_size = 2;
|
||||
int patch_size = 2;
|
||||
int64_t in_channels = 16;
|
||||
int64_t d_self = -1; // >=0 for MMdiT-X
|
||||
int64_t depth = 24;
|
||||
@@ -870,7 +863,7 @@ struct MMDiTRunner : public GGMLRunner {
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* y,
|
||||
std::vector<int> skip_layers = std::vector<int>()) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph_custom(compute_ctx, MMDIT_GRAPH_SIZE, false);
|
||||
struct ggml_cgraph* gf = new_graph_custom(MMDIT_GRAPH_SIZE);
|
||||
|
||||
x = to_backend(x);
|
||||
context = to_backend(context);
|
||||
@@ -890,7 +883,7 @@ struct MMDiTRunner : public GGMLRunner {
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
bool compute(int n_threads,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
@@ -906,7 +899,7 @@ struct MMDiTRunner : public GGMLRunner {
|
||||
return build_graph(x, timesteps, context, y, skip_layers);
|
||||
};
|
||||
|
||||
GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
return GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
|
||||
void test() {
|
||||
@@ -939,12 +932,12 @@ struct MMDiTRunner : public GGMLRunner {
|
||||
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int t0 = ggml_time_ms();
|
||||
int64_t t0 = ggml_time_ms();
|
||||
compute(8, x, timesteps, context, y, &out, work_ctx);
|
||||
int t1 = ggml_time_ms();
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("mmdit test done in %dms", t1 - t0);
|
||||
LOG_DEBUG("mmdit test done in %lldms", t1 - t0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -979,4 +972,4 @@ struct MMDiTRunner : public GGMLRunner {
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "stable-diffusion.h"
|
||||
#include "util.h"
|
||||
#include "vocab.hpp"
|
||||
#include "vocab_mistral.hpp"
|
||||
#include "vocab_qwen.hpp"
|
||||
#include "vocab_umt5.hpp"
|
||||
|
||||
@@ -102,10 +103,15 @@ const char* unused_tensors[] = {
|
||||
"model_ema.diffusion_model",
|
||||
"embedding_manager",
|
||||
"denoiser.sigmas",
|
||||
"edm_vpred.sigma_max",
|
||||
"text_encoders.t5xxl.transformer.encoder.embed_tokens.weight", // only used during training
|
||||
"text_encoders.qwen2vl.output.weight",
|
||||
"text_encoders.qwen2vl.lm_head.",
|
||||
"ztsnr", // Found in some SDXL vpred models
|
||||
"edm_vpred.sigma_min", // Found in CosXL
|
||||
// TODO: find another way to avoid the "unknown tensor" for these two
|
||||
// "edm_vpred.sigma_max", // Used to detect CosXL
|
||||
// "v_pred", // Used to detect SDXL vpred models
|
||||
"text_encoders.llm.output.weight",
|
||||
"text_encoders.llm.lm_head.",
|
||||
"first_stage_model.bn.",
|
||||
};
|
||||
|
||||
bool is_unused_tensor(std::string name) {
|
||||
@@ -117,11 +123,6 @@ bool is_unused_tensor(std::string name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float bf16_to_f32(uint16_t bfloat16) {
|
||||
uint32_t val_bits = (static_cast<uint32_t>(bfloat16) << 16);
|
||||
return *reinterpret_cast<float*>(&val_bits);
|
||||
}
|
||||
|
||||
uint16_t f8_e4m3_to_f16(uint8_t f8) {
|
||||
// do we need to support uz?
|
||||
|
||||
@@ -204,13 +205,6 @@ uint16_t f8_e5m2_to_f16(uint8_t fp8) {
|
||||
return fp16_sign | (fp16_exponent << 10) | fp16_mantissa;
|
||||
}
|
||||
|
||||
void bf16_to_f32_vec(uint16_t* src, float* dst, int64_t n) {
|
||||
// support inplace op
|
||||
for (int64_t i = n - 1; i >= 0; i--) {
|
||||
dst[i] = bf16_to_f32(src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void f8_e4m3_to_f16_vec(uint8_t* src, uint16_t* dst, int64_t n) {
|
||||
// support inplace op
|
||||
for (int64_t i = n - 1; i >= 0; i--) {
|
||||
@@ -263,8 +257,8 @@ void convert_tensor(void* src,
|
||||
} else {
|
||||
auto qtype = ggml_get_type_traits(src_type);
|
||||
if (qtype->to_float == nullptr) {
|
||||
throw std::runtime_error(format("type %s unsupported for integer quantization: no dequantization available",
|
||||
ggml_type_name(src_type)));
|
||||
throw std::runtime_error(sd_format("type %s unsupported for integer quantization: no dequantization available",
|
||||
ggml_type_name(src_type)));
|
||||
}
|
||||
qtype->to_float(src, (float*)dst, n);
|
||||
}
|
||||
@@ -273,8 +267,8 @@ void convert_tensor(void* src,
|
||||
// src_type is quantized => dst_type == GGML_TYPE_F16 or dst_type is quantized
|
||||
auto qtype = ggml_get_type_traits(src_type);
|
||||
if (qtype->to_float == nullptr) {
|
||||
throw std::runtime_error(format("type %s unsupported for integer quantization: no dequantization available",
|
||||
ggml_type_name(src_type)));
|
||||
throw std::runtime_error(sd_format("type %s unsupported for integer quantization: no dequantization available",
|
||||
ggml_type_name(src_type)));
|
||||
}
|
||||
std::vector<char> buf;
|
||||
buf.resize(sizeof(float) * n);
|
||||
@@ -382,7 +376,11 @@ bool ModelLoader::init_from_file(const std::string& file_path, const std::string
|
||||
LOG_INFO("load %s using checkpoint format", file_path.c_str());
|
||||
return init_from_ckpt_file(file_path, prefix);
|
||||
} else {
|
||||
LOG_WARN("unknown format %s", file_path.c_str());
|
||||
if (file_exists(file_path)) {
|
||||
LOG_WARN("unknown format %s", file_path.c_str());
|
||||
} else {
|
||||
LOG_WARN("file %s not found", file_path.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -442,7 +440,7 @@ bool ModelLoader::init_from_gguf_file(const std::string& file_path, const std::s
|
||||
name,
|
||||
gguf_tensor_info.type,
|
||||
gguf_tensor_info.shape.data(),
|
||||
gguf_tensor_info.shape.size(),
|
||||
static_cast<int>(gguf_tensor_info.shape.size()),
|
||||
file_index,
|
||||
data_offset + gguf_tensor_info.offset);
|
||||
|
||||
@@ -454,7 +452,7 @@ bool ModelLoader::init_from_gguf_file(const std::string& file_path, const std::s
|
||||
return true;
|
||||
}
|
||||
|
||||
int n_tensors = gguf_get_n_tensors(ctx_gguf_);
|
||||
int n_tensors = static_cast<int>(gguf_get_n_tensors(ctx_gguf_));
|
||||
|
||||
size_t total_size = 0;
|
||||
size_t data_offset = gguf_get_data_offset(ctx_gguf_);
|
||||
@@ -489,7 +487,7 @@ ggml_type str_to_ggml_type(const std::string& dtype) {
|
||||
if (dtype == "F16") {
|
||||
ttype = GGML_TYPE_F16;
|
||||
} else if (dtype == "BF16") {
|
||||
ttype = GGML_TYPE_F32;
|
||||
ttype = GGML_TYPE_BF16;
|
||||
} else if (dtype == "F32") {
|
||||
ttype = GGML_TYPE_F32;
|
||||
} else if (dtype == "F64") {
|
||||
@@ -617,10 +615,7 @@ bool ModelLoader::init_from_safetensors_file(const std::string& file_path, const
|
||||
|
||||
size_t tensor_data_size = end - begin;
|
||||
|
||||
if (dtype == "BF16") {
|
||||
tensor_storage.is_bf16 = true;
|
||||
GGML_ASSERT(tensor_storage.nbytes() == tensor_data_size * 2);
|
||||
} else if (dtype == "F8_E4M3") {
|
||||
if (dtype == "F8_E4M3") {
|
||||
tensor_storage.is_f8_e4m3 = true;
|
||||
// f8 -> f16
|
||||
GGML_ASSERT(tensor_storage.nbytes() == tensor_data_size * 2);
|
||||
@@ -1043,10 +1038,14 @@ SDVersion ModelLoader::get_sd_version() {
|
||||
|
||||
bool is_xl = false;
|
||||
bool is_flux = false;
|
||||
bool is_flux2 = false;
|
||||
bool has_single_block_47 = false;
|
||||
bool is_wan = false;
|
||||
int64_t patch_embedding_channels = 0;
|
||||
bool has_img_emb = false;
|
||||
bool has_middle_block_1 = false;
|
||||
bool has_output_block_311 = false;
|
||||
bool has_output_block_71 = false;
|
||||
|
||||
for (auto& [name, tensor_storage] : tensor_storage_map) {
|
||||
if (!(is_xl)) {
|
||||
@@ -1062,6 +1061,18 @@ SDVersion ModelLoader::get_sd_version() {
|
||||
if (tensor_storage.name.find("model.diffusion_model.transformer_blocks.0.img_mod.1.weight") != std::string::npos) {
|
||||
return VERSION_QWEN_IMAGE;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.double_stream_modulation_img.lin.weight") != std::string::npos) {
|
||||
is_flux2 = true;
|
||||
}
|
||||
if (tensor_storage.name.find("single_blocks.47.linear1.weight") != std::string::npos) {
|
||||
has_single_block_47 = true;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.double_blocks.0.img_mlp.gate_proj.weight") != std::string::npos) {
|
||||
return VERSION_OVIS_IMAGE;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.cap_embedder.0.weight") != std::string::npos) {
|
||||
return VERSION_Z_IMAGE;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.blocks.0.cross_attn.norm_k.weight") != std::string::npos) {
|
||||
is_wan = true;
|
||||
}
|
||||
@@ -1094,6 +1105,12 @@ SDVersion ModelLoader::get_sd_version() {
|
||||
tensor_storage.name.find("unet.mid_block.resnets.1.") != std::string::npos) {
|
||||
has_middle_block_1 = true;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.output_blocks.3.1.transformer_blocks.1") != std::string::npos) {
|
||||
has_output_block_311 = true;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.output_blocks.7.1") != std::string::npos) {
|
||||
has_output_block_71 = true;
|
||||
}
|
||||
if (tensor_storage.name == "cond_stage_model.transformer.text_model.embeddings.token_embedding.weight" ||
|
||||
tensor_storage.name == "cond_stage_model.model.token_embedding.weight" ||
|
||||
tensor_storage.name == "text_model.embeddings.token_embedding.weight" ||
|
||||
@@ -1129,12 +1146,15 @@ SDVersion ModelLoader::get_sd_version() {
|
||||
return VERSION_SDXL_PIX2PIX;
|
||||
}
|
||||
if (!has_middle_block_1) {
|
||||
if (!has_output_block_311) {
|
||||
return VERSION_SDXL_VEGA;
|
||||
}
|
||||
return VERSION_SDXL_SSD1B;
|
||||
}
|
||||
return VERSION_SDXL;
|
||||
}
|
||||
|
||||
if (is_flux) {
|
||||
if (is_flux && !is_flux2) {
|
||||
if (input_block_weight.ne[0] == 384) {
|
||||
return VERSION_FLUX_FILL;
|
||||
}
|
||||
@@ -1147,6 +1167,13 @@ SDVersion ModelLoader::get_sd_version() {
|
||||
return VERSION_FLUX;
|
||||
}
|
||||
|
||||
if (is_flux2) {
|
||||
if (has_single_block_47) {
|
||||
return VERSION_FLUX2;
|
||||
}
|
||||
return VERSION_FLUX2_KLEIN;
|
||||
}
|
||||
|
||||
if (token_embedding_weight.ne[0] == 768) {
|
||||
if (is_inpaint) {
|
||||
return VERSION_SD1_INPAINT;
|
||||
@@ -1155,6 +1182,9 @@ SDVersion ModelLoader::get_sd_version() {
|
||||
return VERSION_SD1_PIX2PIX;
|
||||
}
|
||||
if (!has_middle_block_1) {
|
||||
if (!has_output_block_71) {
|
||||
return VERSION_SDXS;
|
||||
}
|
||||
return VERSION_SD1_TINY_UNET;
|
||||
}
|
||||
return VERSION_SD1;
|
||||
@@ -1254,15 +1284,59 @@ std::map<ggml_type, uint32_t> ModelLoader::get_vae_wtype_stat() {
|
||||
return wtype_stat;
|
||||
}
|
||||
|
||||
void ModelLoader::set_wtype_override(ggml_type wtype, std::string prefix) {
|
||||
static std::vector<std::pair<std::string, ggml_type>> parse_tensor_type_rules(const std::string& tensor_type_rules) {
|
||||
std::vector<std::pair<std::string, ggml_type>> result;
|
||||
for (const auto& item : split_string(tensor_type_rules, ',')) {
|
||||
if (item.size() == 0)
|
||||
continue;
|
||||
std::string::size_type pos = item.find('=');
|
||||
if (pos == std::string::npos) {
|
||||
LOG_WARN("ignoring invalid quant override \"%s\"", item.c_str());
|
||||
continue;
|
||||
}
|
||||
std::string tensor_pattern = item.substr(0, pos);
|
||||
std::string type_name = item.substr(pos + 1);
|
||||
|
||||
ggml_type tensor_type = GGML_TYPE_COUNT;
|
||||
|
||||
if (type_name == "f32") {
|
||||
tensor_type = GGML_TYPE_F32;
|
||||
} else {
|
||||
for (size_t i = 0; i < GGML_TYPE_COUNT; i++) {
|
||||
auto trait = ggml_get_type_traits((ggml_type)i);
|
||||
if (trait->to_float && trait->type_size && type_name == trait->type_name) {
|
||||
tensor_type = (ggml_type)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tensor_type != GGML_TYPE_COUNT) {
|
||||
result.emplace_back(tensor_pattern, tensor_type);
|
||||
} else {
|
||||
LOG_WARN("ignoring invalid quant override \"%s\"", item.c_str());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ModelLoader::set_wtype_override(ggml_type wtype, std::string tensor_type_rules) {
|
||||
auto map_rules = parse_tensor_type_rules(tensor_type_rules);
|
||||
for (auto& [name, tensor_storage] : tensor_storage_map) {
|
||||
if (!starts_with(name, prefix)) {
|
||||
ggml_type dst_type = wtype;
|
||||
for (const auto& tensor_type_rule : map_rules) {
|
||||
std::regex pattern(tensor_type_rule.first);
|
||||
if (std::regex_search(name, pattern)) {
|
||||
dst_type = tensor_type_rule.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dst_type == GGML_TYPE_COUNT) {
|
||||
continue;
|
||||
}
|
||||
if (!tensor_should_be_converted(tensor_storage, wtype)) {
|
||||
if (!tensor_should_be_converted(tensor_storage, dst_type)) {
|
||||
continue;
|
||||
}
|
||||
tensor_storage.expected_type = wtype;
|
||||
tensor_storage.expected_type = dst_type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1276,6 +1350,16 @@ std::string ModelLoader::load_qwen2_merges() {
|
||||
return merges_utf8_str;
|
||||
}
|
||||
|
||||
std::string ModelLoader::load_mistral_merges() {
|
||||
std::string merges_utf8_str(reinterpret_cast<const char*>(mistral_merges_utf8_c_str), sizeof(mistral_merges_utf8_c_str));
|
||||
return merges_utf8_str;
|
||||
}
|
||||
|
||||
std::string ModelLoader::load_mistral_vocab_json() {
|
||||
std::string json_str(reinterpret_cast<const char*>(mistral_vocab_json_utf8_c_str), sizeof(mistral_vocab_json_utf8_c_str));
|
||||
return json_str;
|
||||
}
|
||||
|
||||
std::string ModelLoader::load_t5_tokenizer_json() {
|
||||
std::string json_str(reinterpret_cast<const char*>(t5_tokenizer_json_str), sizeof(t5_tokenizer_json_str));
|
||||
return json_str;
|
||||
@@ -1286,14 +1370,14 @@ std::string ModelLoader::load_umt5_tokenizer_json() {
|
||||
return json_str;
|
||||
}
|
||||
|
||||
bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_threads_p) {
|
||||
bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_threads_p, bool enable_mmap) {
|
||||
int64_t process_time_ms = 0;
|
||||
std::atomic<int64_t> read_time_ms(0);
|
||||
std::atomic<int64_t> memcpy_time_ms(0);
|
||||
std::atomic<int64_t> copy_to_backend_time_ms(0);
|
||||
std::atomic<int64_t> convert_time_ms(0);
|
||||
|
||||
int num_threads_to_use = n_threads_p > 0 ? n_threads_p : get_num_physical_cores();
|
||||
int num_threads_to_use = n_threads_p > 0 ? n_threads_p : sd_get_num_physical_cores();
|
||||
LOG_DEBUG("using %d threads for model loading", num_threads_to_use);
|
||||
|
||||
int64_t start_time = ggml_time_ms();
|
||||
@@ -1336,6 +1420,15 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<MmapWrapper> mmapped;
|
||||
if (enable_mmap && !is_zip) {
|
||||
LOG_DEBUG("using mmap for I/O");
|
||||
mmapped = MmapWrapper::create(file_path);
|
||||
if (!mmapped) {
|
||||
LOG_WARN("failed to memory-map '%s'", file_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int n_threads = is_zip ? 1 : std::min(num_threads_to_use, (int)file_tensors.size());
|
||||
if (n_threads < 1) {
|
||||
n_threads = 1;
|
||||
@@ -1357,7 +1450,7 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
} else if (!mmapped) {
|
||||
file.open(file_path, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
LOG_ERROR("failed to open '%s'", file_path.c_str());
|
||||
@@ -1410,6 +1503,11 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
zip_entry_noallocread(zip, (void*)buf, n);
|
||||
}
|
||||
zip_entry_close(zip);
|
||||
} else if (mmapped) {
|
||||
if (!mmapped->copy_data(buf, n, tensor_storage.offset)) {
|
||||
LOG_ERROR("read tensor data failed: '%s'", file_path.c_str());
|
||||
failed = true;
|
||||
}
|
||||
} else {
|
||||
file.seekg(tensor_storage.offset);
|
||||
file.read(buf, n);
|
||||
@@ -1456,9 +1554,7 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
read_time_ms.fetch_add(t1 - t0);
|
||||
|
||||
t0 = ggml_time_ms();
|
||||
if (tensor_storage.is_bf16) {
|
||||
bf16_to_f32_vec((uint16_t*)read_buf, (float*)target_buf, tensor_storage.nelements());
|
||||
} else if (tensor_storage.is_f8_e4m3) {
|
||||
if (tensor_storage.is_f8_e4m3) {
|
||||
f8_e4m3_to_f16_vec((uint8_t*)read_buf, (uint16_t*)target_buf, tensor_storage.nelements());
|
||||
} else if (tensor_storage.is_f8_e5m2) {
|
||||
f8_e5m2_to_f16_vec((uint8_t*)read_buf, (uint16_t*)target_buf, tensor_storage.nelements());
|
||||
@@ -1468,6 +1564,11 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
i64_to_i32_vec((int64_t*)read_buf, (int32_t*)target_buf, tensor_storage.nelements());
|
||||
}
|
||||
if (tensor_storage.type != dst_tensor->type) {
|
||||
if (convert_buf == nullptr) {
|
||||
LOG_ERROR("read tensor data failed: too less memory for conversion");
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
convert_tensor((void*)target_buf,
|
||||
tensor_storage.type,
|
||||
convert_buf,
|
||||
@@ -1499,7 +1600,7 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
break;
|
||||
}
|
||||
size_t curr_num = total_tensors_processed + current_idx;
|
||||
pretty_progress(curr_num, total_tensors_to_process, (ggml_time_ms() - t_start) / 1000.0f / (curr_num + 1e-6f));
|
||||
pretty_progress(static_cast<int>(curr_num), static_cast<int>(total_tensors_to_process), (ggml_time_ms() - t_start) / 1000.0f / (curr_num + 1e-6f));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
}
|
||||
|
||||
@@ -1512,7 +1613,7 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
break;
|
||||
}
|
||||
total_tensors_processed += file_tensors.size();
|
||||
pretty_progress(total_tensors_processed, total_tensors_to_process, (ggml_time_ms() - t_start) / 1000.0f / (total_tensors_processed + 1e-6f));
|
||||
pretty_progress(static_cast<int>(total_tensors_processed), static_cast<int>(total_tensors_to_process), (ggml_time_ms() - t_start) / 1000.0f / (total_tensors_processed + 1e-6f));
|
||||
if (total_tensors_processed < total_tensors_to_process) {
|
||||
printf("\n");
|
||||
}
|
||||
@@ -1531,7 +1632,8 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
|
||||
bool ModelLoader::load_tensors(std::map<std::string, struct ggml_tensor*>& tensors,
|
||||
std::set<std::string> ignore_tensors,
|
||||
int n_threads) {
|
||||
int n_threads,
|
||||
bool enable_mmap) {
|
||||
std::set<std::string> tensor_names_in_file;
|
||||
std::mutex tensor_names_mutex;
|
||||
auto on_new_tensor_cb = [&](const TensorStorage& tensor_storage, ggml_tensor** dst_tensor) -> bool {
|
||||
@@ -1574,7 +1676,7 @@ bool ModelLoader::load_tensors(std::map<std::string, struct ggml_tensor*>& tenso
|
||||
return true;
|
||||
};
|
||||
|
||||
bool success = load_tensors(on_new_tensor_cb, n_threads);
|
||||
bool success = load_tensors(on_new_tensor_cb, n_threads, enable_mmap);
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from file failed");
|
||||
return false;
|
||||
@@ -1603,41 +1705,6 @@ bool ModelLoader::load_tensors(std::map<std::string, struct ggml_tensor*>& tenso
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, ggml_type>> parse_tensor_type_rules(const std::string& tensor_type_rules) {
|
||||
std::vector<std::pair<std::string, ggml_type>> result;
|
||||
for (const auto& item : split_string(tensor_type_rules, ',')) {
|
||||
if (item.size() == 0)
|
||||
continue;
|
||||
std::string::size_type pos = item.find('=');
|
||||
if (pos == std::string::npos) {
|
||||
LOG_WARN("ignoring invalid quant override \"%s\"", item.c_str());
|
||||
continue;
|
||||
}
|
||||
std::string tensor_pattern = item.substr(0, pos);
|
||||
std::string type_name = item.substr(pos + 1);
|
||||
|
||||
ggml_type tensor_type = GGML_TYPE_COUNT;
|
||||
|
||||
if (type_name == "f32") {
|
||||
tensor_type = GGML_TYPE_F32;
|
||||
} else {
|
||||
for (size_t i = 0; i < GGML_TYPE_COUNT; i++) {
|
||||
auto trait = ggml_get_type_traits((ggml_type)i);
|
||||
if (trait->to_float && trait->type_size && type_name == trait->type_name) {
|
||||
tensor_type = (ggml_type)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tensor_type != GGML_TYPE_COUNT) {
|
||||
result.emplace_back(tensor_pattern, tensor_type);
|
||||
} else {
|
||||
LOG_WARN("ignoring invalid quant override \"%s\"", item.c_str());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ModelLoader::tensor_should_be_converted(const TensorStorage& tensor_storage, ggml_type type) {
|
||||
const std::string& name = tensor_storage.name;
|
||||
if (type != GGML_TYPE_COUNT) {
|
||||
@@ -1715,6 +1782,13 @@ bool ModelLoader::save_to_gguf_file(const std::string& file_path, ggml_type type
|
||||
// tensor_storage.ne[0], tensor_storage.ne[1], tensor_storage.ne[2], tensor_storage.ne[3],
|
||||
// tensor->n_dims, tensor->ne[0], tensor->ne[1], tensor->ne[2], tensor->ne[3]);
|
||||
|
||||
if (!tensor->data) {
|
||||
GGML_ASSERT(ggml_nelements(tensor) == 0);
|
||||
// avoid crashing the gguf writer by setting a dummy pointer for zero-sized tensors
|
||||
LOG_DEBUG("setting dummy pointer for zero-sized tensor %s", name.c_str());
|
||||
tensor->data = ggml_get_mem_buffer(ggml_ctx);
|
||||
}
|
||||
|
||||
*dst_tensor = tensor;
|
||||
|
||||
gguf_add_tensor(gguf_ctx, tensor);
|
||||
@@ -1754,7 +1828,12 @@ int64_t ModelLoader::get_params_mem_size(ggml_backend_t backend, ggml_type type)
|
||||
return mem_size;
|
||||
}
|
||||
|
||||
bool convert(const char* input_path, const char* vae_path, const char* output_path, sd_type_t output_type, const char* tensor_type_rules) {
|
||||
bool convert(const char* input_path,
|
||||
const char* vae_path,
|
||||
const char* output_path,
|
||||
sd_type_t output_type,
|
||||
const char* tensor_type_rules,
|
||||
bool convert_name) {
|
||||
ModelLoader model_loader;
|
||||
|
||||
if (!model_loader.init_from_file(input_path)) {
|
||||
@@ -1768,7 +1847,9 @@ bool convert(const char* input_path, const char* vae_path, const char* output_pa
|
||||
return false;
|
||||
}
|
||||
}
|
||||
model_loader.convert_tensors_name();
|
||||
if (convert_name) {
|
||||
model_loader.convert_tensors_name();
|
||||
}
|
||||
bool success = model_loader.save_to_gguf_file(output_path, (ggml_type)output_type, tensor_type_rules);
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -28,9 +28,11 @@ enum SDVersion {
|
||||
VERSION_SD2,
|
||||
VERSION_SD2_INPAINT,
|
||||
VERSION_SD2_TINY_UNET,
|
||||
VERSION_SDXS,
|
||||
VERSION_SDXL,
|
||||
VERSION_SDXL_INPAINT,
|
||||
VERSION_SDXL_PIX2PIX,
|
||||
VERSION_SDXL_VEGA,
|
||||
VERSION_SDXL_SSD1B,
|
||||
VERSION_SVD,
|
||||
VERSION_SD3,
|
||||
@@ -43,11 +45,15 @@ enum SDVersion {
|
||||
VERSION_WAN2_2_I2V,
|
||||
VERSION_WAN2_2_TI2V,
|
||||
VERSION_QWEN_IMAGE,
|
||||
VERSION_FLUX2,
|
||||
VERSION_FLUX2_KLEIN,
|
||||
VERSION_Z_IMAGE,
|
||||
VERSION_OVIS_IMAGE,
|
||||
VERSION_COUNT,
|
||||
};
|
||||
|
||||
static inline bool sd_version_is_sd1(SDVersion version) {
|
||||
if (version == VERSION_SD1 || version == VERSION_SD1_INPAINT || version == VERSION_SD1_PIX2PIX || version == VERSION_SD1_TINY_UNET) {
|
||||
if (version == VERSION_SD1 || version == VERSION_SD1_INPAINT || version == VERSION_SD1_PIX2PIX || version == VERSION_SD1_TINY_UNET || version == VERSION_SDXS) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -61,7 +67,7 @@ static inline bool sd_version_is_sd2(SDVersion version) {
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_sdxl(SDVersion version) {
|
||||
if (version == VERSION_SDXL || version == VERSION_SDXL_INPAINT || version == VERSION_SDXL_PIX2PIX || version == VERSION_SDXL_SSD1B) {
|
||||
if (version == VERSION_SDXL || version == VERSION_SDXL_INPAINT || version == VERSION_SDXL_PIX2PIX || version == VERSION_SDXL_SSD1B || version == VERSION_SDXL_VEGA) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -88,12 +94,20 @@ static inline bool sd_version_is_flux(SDVersion version) {
|
||||
version == VERSION_FLUX_FILL ||
|
||||
version == VERSION_FLUX_CONTROLS ||
|
||||
version == VERSION_FLEX_2 ||
|
||||
version == VERSION_OVIS_IMAGE ||
|
||||
version == VERSION_CHROMA_RADIANCE) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_flux2(SDVersion version) {
|
||||
if (version == VERSION_FLUX2 || version == VERSION_FLUX2_KLEIN) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_wan(SDVersion version) {
|
||||
if (version == VERSION_WAN2 || version == VERSION_WAN2_2_I2V || version == VERSION_WAN2_2_TI2V) {
|
||||
return true;
|
||||
@@ -108,6 +122,13 @@ static inline bool sd_version_is_qwen_image(SDVersion version) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_z_image(SDVersion version) {
|
||||
if (version == VERSION_Z_IMAGE) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_inpaint(SDVersion version) {
|
||||
if (version == VERSION_SD1_INPAINT ||
|
||||
version == VERSION_SD2_INPAINT ||
|
||||
@@ -121,9 +142,11 @@ static inline bool sd_version_is_inpaint(SDVersion version) {
|
||||
|
||||
static inline bool sd_version_is_dit(SDVersion version) {
|
||||
if (sd_version_is_flux(version) ||
|
||||
sd_version_is_flux2(version) ||
|
||||
sd_version_is_sd3(version) ||
|
||||
sd_version_is_wan(version) ||
|
||||
sd_version_is_qwen_image(version)) {
|
||||
sd_version_is_qwen_image(version) ||
|
||||
sd_version_is_z_image(version)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -150,7 +173,6 @@ struct TensorStorage {
|
||||
std::string name;
|
||||
ggml_type type = GGML_TYPE_F32;
|
||||
ggml_type expected_type = GGML_TYPE_COUNT;
|
||||
bool is_bf16 = false;
|
||||
bool is_f8_e4m3 = false;
|
||||
bool is_f8_e5m2 = false;
|
||||
bool is_f64 = false;
|
||||
@@ -184,7 +206,7 @@ struct TensorStorage {
|
||||
}
|
||||
|
||||
int64_t nbytes_to_read() const {
|
||||
if (is_bf16 || is_f8_e4m3 || is_f8_e5m2) {
|
||||
if (is_f8_e4m3 || is_f8_e5m2) {
|
||||
return nbytes() / 2;
|
||||
} else if (is_f64 || is_i64) {
|
||||
return nbytes() * 2;
|
||||
@@ -232,9 +254,7 @@ struct TensorStorage {
|
||||
std::string to_string() const {
|
||||
std::stringstream ss;
|
||||
const char* type_name = ggml_type_name(type);
|
||||
if (is_bf16) {
|
||||
type_name = "bf16";
|
||||
} else if (is_f8_e4m3) {
|
||||
if (is_f8_e4m3) {
|
||||
type_name = "f8_e4m3";
|
||||
} else if (is_f8_e5m2) {
|
||||
type_name = "f8_e5m2";
|
||||
@@ -292,11 +312,12 @@ public:
|
||||
std::map<ggml_type, uint32_t> get_diffusion_model_wtype_stat();
|
||||
std::map<ggml_type, uint32_t> get_vae_wtype_stat();
|
||||
String2TensorStorage& get_tensor_storage_map() { return tensor_storage_map; }
|
||||
void set_wtype_override(ggml_type wtype, std::string prefix = "");
|
||||
bool load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_threads = 0);
|
||||
void set_wtype_override(ggml_type wtype, std::string tensor_type_rules = "");
|
||||
bool load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_threads = 0, bool use_mmap = false);
|
||||
bool load_tensors(std::map<std::string, struct ggml_tensor*>& tensors,
|
||||
std::set<std::string> ignore_tensors = {},
|
||||
int n_threads = 0);
|
||||
int n_threads = 0,
|
||||
bool use_mmap = false);
|
||||
|
||||
std::vector<std::string> get_tensor_names() const {
|
||||
std::vector<std::string> names;
|
||||
@@ -313,6 +334,8 @@ public:
|
||||
|
||||
static std::string load_merges();
|
||||
static std::string load_qwen2_merges();
|
||||
static std::string load_mistral_merges();
|
||||
static std::string load_mistral_vocab_json();
|
||||
static std::string load_t5_tokenizer_json();
|
||||
static std::string load_umt5_tokenizer_json();
|
||||
};
|
||||
|
||||
@@ -127,12 +127,14 @@ std::string convert_cond_stage_model_name(std::string name, std::string prefix)
|
||||
{"token_embd.", "shared."},
|
||||
};
|
||||
|
||||
static const std::vector<std::pair<std::string, std::string>> qwenvl_name_map{
|
||||
static const std::vector<std::pair<std::string, std::string>> llm_name_map{
|
||||
{"token_embd.", "model.embed_tokens."},
|
||||
{"blk.", "model.layers."},
|
||||
{"attn_q.", "self_attn.q_proj."},
|
||||
{"attn_k.", "self_attn.k_proj."},
|
||||
{"attn_v.", "self_attn.v_proj."},
|
||||
{"attn_q_norm.", "self_attn.q_norm."},
|
||||
{"attn_k_norm.", "self_attn.k_norm."},
|
||||
{"attn_output.", "self_attn.o_proj."},
|
||||
{"attn_norm.", "input_layernorm."},
|
||||
{"ffn_down.", "mlp.down_proj."},
|
||||
@@ -142,7 +144,7 @@ std::string convert_cond_stage_model_name(std::string name, std::string prefix)
|
||||
{"output_norm.", "model.norm."},
|
||||
};
|
||||
|
||||
static const std::vector<std::pair<std::string, std::string>> qwenvl_vision_name_map{
|
||||
static const std::vector<std::pair<std::string, std::string>> llm_vision_name_map{
|
||||
{"mm.", "merger.mlp."},
|
||||
{"v.post_ln.", "merger.ln_q."},
|
||||
{"v.patch_embd.weight", "patch_embed.proj.0.weight"},
|
||||
@@ -161,11 +163,11 @@ std::string convert_cond_stage_model_name(std::string name, std::string prefix)
|
||||
};
|
||||
if (contains(name, "t5xxl")) {
|
||||
replace_with_name_map(name, t5_name_map);
|
||||
} else if (contains(name, "qwen2vl")) {
|
||||
if (contains(name, "qwen2vl.visual")) {
|
||||
replace_with_name_map(name, qwenvl_vision_name_map);
|
||||
} else if (contains(name, "llm")) {
|
||||
if (contains(name, "llm.visual")) {
|
||||
replace_with_name_map(name, llm_vision_name_map);
|
||||
} else {
|
||||
replace_with_name_map(name, qwenvl_name_map);
|
||||
replace_with_name_map(name, llm_name_map);
|
||||
}
|
||||
} else {
|
||||
name = convert_open_clip_to_hf_clip_name(name);
|
||||
@@ -613,6 +615,44 @@ std::string convert_diffusers_dit_to_original_flux(std::string name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string convert_diffusers_dit_to_original_lumina2(std::string name) {
|
||||
int num_layers = 30;
|
||||
int num_refiner_layers = 2;
|
||||
static std::unordered_map<std::string, std::string> z_image_name_map;
|
||||
|
||||
if (z_image_name_map.empty()) {
|
||||
z_image_name_map["all_x_embedder.2-1."] = "x_embedder.";
|
||||
z_image_name_map["all_final_layer.2-1."] = "final_layer.";
|
||||
|
||||
// --- transformer blocks ---
|
||||
auto add_attention_map = [&](const std::string& prefix, int num) {
|
||||
for (int i = 0; i < num; ++i) {
|
||||
std::string block_prefix = prefix + std::to_string(i) + ".";
|
||||
std::string dst_prefix = prefix + std::to_string(i) + ".";
|
||||
|
||||
z_image_name_map[block_prefix + "attention.norm_q."] = dst_prefix + "attention.q_norm.";
|
||||
z_image_name_map[block_prefix + "attention.norm_k."] = dst_prefix + "attention.k_norm.";
|
||||
z_image_name_map[block_prefix + "attention.to_out.0."] = dst_prefix + "attention.out.";
|
||||
|
||||
z_image_name_map[block_prefix + "attention.to_q.weight"] = dst_prefix + "attention.qkv.weight";
|
||||
z_image_name_map[block_prefix + "attention.to_q.bias"] = dst_prefix + "attention.qkv.bias";
|
||||
z_image_name_map[block_prefix + "attention.to_k.weight"] = dst_prefix + "attention.qkv.weight.1";
|
||||
z_image_name_map[block_prefix + "attention.to_k.bias"] = dst_prefix + "attention.qkv.bias.1";
|
||||
z_image_name_map[block_prefix + "attention.to_v.weight"] = dst_prefix + "attention.qkv.weight.2";
|
||||
z_image_name_map[block_prefix + "attention.to_v.bias"] = dst_prefix + "attention.qkv.bias.2";
|
||||
}
|
||||
};
|
||||
|
||||
add_attention_map("noise_refiner.", num_refiner_layers);
|
||||
add_attention_map("context_refiner.", num_refiner_layers);
|
||||
add_attention_map("layers.", num_layers);
|
||||
}
|
||||
|
||||
replace_with_prefix_map(name, z_image_name_map);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string convert_diffusion_model_name(std::string name, std::string prefix, SDVersion version) {
|
||||
if (sd_version_is_sd1(version) || sd_version_is_sd2(version)) {
|
||||
name = convert_diffusers_unet_to_original_sd1(name);
|
||||
@@ -620,8 +660,10 @@ std::string convert_diffusion_model_name(std::string name, std::string prefix, S
|
||||
name = convert_diffusers_unet_to_original_sdxl(name);
|
||||
} else if (sd_version_is_sd3(version)) {
|
||||
name = convert_diffusers_dit_to_original_sd3(name);
|
||||
} else if (sd_version_is_flux(version)) {
|
||||
} else if (sd_version_is_flux(version) || sd_version_is_flux2(version)) {
|
||||
name = convert_diffusers_dit_to_original_flux(name);
|
||||
} else if (sd_version_is_z_image(version)) {
|
||||
name = convert_diffusers_dit_to_original_lumina2(name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
@@ -722,6 +764,11 @@ std::string convert_diffusers_vae_to_original_sd1(std::string name) {
|
||||
}
|
||||
|
||||
std::string convert_first_stage_model_name(std::string name, std::string prefix) {
|
||||
static std::unordered_map<std::string, std::string> vae_name_map = {
|
||||
{"decoder.post_quant_conv.", "post_quant_conv."},
|
||||
{"encoder.quant_conv.", "quant_conv."},
|
||||
};
|
||||
replace_with_prefix_map(name, vae_name_map);
|
||||
name = convert_diffusers_vae_to_original_sd1(name);
|
||||
return name;
|
||||
}
|
||||
@@ -788,12 +835,14 @@ std::string convert_sep_to_dot(std::string name) {
|
||||
"proj_out",
|
||||
"transformer_blocks",
|
||||
"single_transformer_blocks",
|
||||
"single_blocks",
|
||||
"diffusion_model",
|
||||
"cond_stage_model",
|
||||
"first_stage_model",
|
||||
"conv_in",
|
||||
"conv_out",
|
||||
"lora_down",
|
||||
"lora_mid",
|
||||
"lora_up",
|
||||
"diff_b",
|
||||
"hada_w1_a",
|
||||
@@ -829,7 +878,18 @@ std::string convert_sep_to_dot(std::string name) {
|
||||
"ff_context",
|
||||
"norm_added_q",
|
||||
"norm_added_v",
|
||||
"to_add_out"};
|
||||
"to_add_out",
|
||||
"txt_mod",
|
||||
"img_mod",
|
||||
"txt_mlp",
|
||||
"img_mlp",
|
||||
"proj_mlp",
|
||||
"wi_0",
|
||||
"wi_1",
|
||||
"norm1_context",
|
||||
"ff_context",
|
||||
"x_embedder",
|
||||
};
|
||||
|
||||
// record the positions of underscores that should NOT be replaced
|
||||
std::unordered_set<size_t> protected_positions;
|
||||
@@ -855,9 +915,53 @@ std::string convert_sep_to_dot(std::string name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
std::vector<std::string> cond_stage_model_prefix_vec = {
|
||||
"cond_stage_model.1.",
|
||||
"cond_stage_model.",
|
||||
"conditioner.embedders.",
|
||||
"text_encoders.",
|
||||
};
|
||||
|
||||
std::vector<std::string> diffuison_model_prefix_vec = {
|
||||
"model.diffusion_model.",
|
||||
};
|
||||
|
||||
std::vector<std::string> first_stage_model_prefix_vec = {
|
||||
"first_stage_model.",
|
||||
"vae.",
|
||||
};
|
||||
|
||||
bool is_cond_stage_model_name(const std::string& name) {
|
||||
for (const auto& prefix : cond_stage_model_prefix_vec) {
|
||||
if (starts_with(name, prefix) || starts_with(name, "lora." + prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_diffusion_model_name(const std::string& name) {
|
||||
for (const auto& prefix : diffuison_model_prefix_vec) {
|
||||
if (starts_with(name, prefix) || starts_with(name, "lora." + prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_first_stage_model_name(const std::string& name) {
|
||||
for (const auto& prefix : first_stage_model_prefix_vec) {
|
||||
if (starts_with(name, prefix) || starts_with(name, "lora." + prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string convert_tensor_name(std::string name, SDVersion version) {
|
||||
bool is_lora = false;
|
||||
bool is_lycoris_underline = false;
|
||||
bool is_underline = false;
|
||||
std::vector<std::string> lora_prefix_vec = {
|
||||
"lora.lora.",
|
||||
"lora.lora_",
|
||||
@@ -865,12 +969,27 @@ std::string convert_tensor_name(std::string name, SDVersion version) {
|
||||
"lora.lycoris.",
|
||||
"lora.",
|
||||
};
|
||||
std::vector<std::string> underline_lora_prefix_vec = {
|
||||
"unet_",
|
||||
"te_",
|
||||
"te1_",
|
||||
"te2_",
|
||||
"te3_",
|
||||
"vae_",
|
||||
};
|
||||
for (const auto& prefix : lora_prefix_vec) {
|
||||
if (starts_with(name, prefix)) {
|
||||
is_lora = true;
|
||||
name = name.substr(prefix.size());
|
||||
if (contains(prefix, "lycoris_")) {
|
||||
is_lycoris_underline = true;
|
||||
} else {
|
||||
for (const auto& underline_lora_prefix : underline_lora_prefix_vec) {
|
||||
if (starts_with(name, underline_lora_prefix)) {
|
||||
is_underline = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -879,10 +998,13 @@ std::string convert_tensor_name(std::string name, SDVersion version) {
|
||||
if (is_lora) {
|
||||
std::map<std::string, std::string> lora_suffix_map = {
|
||||
{".lora_down.weight", ".weight.lora_down"},
|
||||
{".lora_mid.weight", ".weight.lora_mid"},
|
||||
{".lora_up.weight", ".weight.lora_up"},
|
||||
{".lora.down.weight", ".weight.lora_down"},
|
||||
{".lora.mid.weight", ".weight.lora_mid"},
|
||||
{".lora.up.weight", ".weight.lora_up"},
|
||||
{"_lora.down.weight", ".weight.lora_down"},
|
||||
{"_lora.mid.weight", ".weight.lora_mid"},
|
||||
{"_lora.up.weight", ".weight.lora_up"},
|
||||
{".lora_A.weight", ".weight.lora_down"},
|
||||
{".lora_B.weight", ".weight.lora_up"},
|
||||
@@ -930,12 +1052,14 @@ std::string convert_tensor_name(std::string name, SDVersion version) {
|
||||
}
|
||||
}
|
||||
|
||||
if (sd_version_is_unet(version) || is_lycoris_underline) {
|
||||
// LOG_DEBUG("name %s %d", name.c_str(), version);
|
||||
|
||||
if (sd_version_is_unet(version) || is_underline || is_lycoris_underline) {
|
||||
name = convert_sep_to_dot(name);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> prefix_map = {
|
||||
std::unordered_map<std::string, std::string> prefix_map = {
|
||||
{"diffusion_model.", "model.diffusion_model."},
|
||||
{"unet.", "model.diffusion_model."},
|
||||
{"transformer.", "model.diffusion_model."}, // dit
|
||||
@@ -950,15 +1074,17 @@ std::string convert_tensor_name(std::string name, SDVersion version) {
|
||||
// {"te2.text_model.encoder.layers.", "cond_stage_model.1.model.transformer.resblocks."},
|
||||
{"te2.", "cond_stage_model.1.transformer."},
|
||||
{"te1.", "cond_stage_model.transformer."},
|
||||
{"te3.", "text_encoders.t5xxl.transformer."},
|
||||
};
|
||||
|
||||
if (sd_version_is_flux(version)) {
|
||||
prefix_map["te1."] = "text_encoders.clip_l.transformer.";
|
||||
}
|
||||
|
||||
replace_with_prefix_map(name, prefix_map);
|
||||
|
||||
// diffusion model
|
||||
{
|
||||
std::vector<std::string> diffuison_model_prefix_vec = {
|
||||
"model.diffusion_model.",
|
||||
};
|
||||
for (const auto& prefix : diffuison_model_prefix_vec) {
|
||||
if (starts_with(name, prefix)) {
|
||||
name = convert_diffusion_model_name(name.substr(prefix.size()), prefix, version);
|
||||
@@ -970,12 +1096,6 @@ std::string convert_tensor_name(std::string name, SDVersion version) {
|
||||
|
||||
// cond_stage_model
|
||||
{
|
||||
std::vector<std::string> cond_stage_model_prefix_vec = {
|
||||
"cond_stage_model.1.",
|
||||
"cond_stage_model.",
|
||||
"conditioner.embedders.",
|
||||
"text_encoders.",
|
||||
};
|
||||
for (const auto& prefix : cond_stage_model_prefix_vec) {
|
||||
if (starts_with(name, prefix)) {
|
||||
name = convert_cond_stage_model_name(name.substr(prefix.size()), prefix);
|
||||
@@ -987,10 +1107,6 @@ std::string convert_tensor_name(std::string name, SDVersion version) {
|
||||
|
||||
// first_stage_model
|
||||
{
|
||||
std::vector<std::string> first_stage_model_prefix_vec = {
|
||||
"first_stage_model.",
|
||||
"vae.",
|
||||
};
|
||||
for (const auto& prefix : first_stage_model_prefix_vec) {
|
||||
if (starts_with(name, prefix)) {
|
||||
name = convert_first_stage_model_name(name.substr(prefix.size()), prefix);
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
#include "model.h"
|
||||
|
||||
bool is_cond_stage_model_name(const std::string& name);
|
||||
bool is_diffusion_model_name(const std::string& name);
|
||||
bool is_first_stage_model_name(const std::string& name);
|
||||
|
||||
std::string convert_tensor_name(std::string name, SDVersion version);
|
||||
|
||||
#endif // __NAME_CONVERSTION_H__
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
x = layer_norm->forward(ctx, x);
|
||||
// x = ggml_add(ctx, ggml_mul_mat(ctx, fc1_w, x), fc1_b);
|
||||
x = fc1->forward(ctx, x);
|
||||
x = ggml_gelu_inplace(ctx->ggml_ctx, x);
|
||||
x = ggml_ext_gelu(ctx->ggml_ctx, x, true);
|
||||
x = fc2->forward(ctx, x);
|
||||
// x = ggml_add(ctx, ggml_mul_mat(ctx, fc2_w, x), fc2_b);
|
||||
if (use_residue)
|
||||
@@ -72,7 +72,7 @@ struct PerceiverAttention : public GGMLBlock {
|
||||
int heads; // = heads
|
||||
public:
|
||||
PerceiverAttention(int dim, int dim_h = 64, int h = 8)
|
||||
: scale(powf(dim_h, -0.5)), dim_head(dim_h), heads(h) {
|
||||
: scale(powf(static_cast<float>(dim_h), -0.5f)), dim_head(dim_h), heads(h) {
|
||||
int inner_dim = dim_head * heads;
|
||||
blocks["norm1"] = std::shared_ptr<GGMLBlock>(new LayerNorm(dim));
|
||||
blocks["norm2"] = std::shared_ptr<GGMLBlock>(new LayerNorm(dim));
|
||||
@@ -129,8 +129,8 @@ public:
|
||||
k = reshape_tensor(ctx->ggml_ctx, k, heads);
|
||||
v = reshape_tensor(ctx->ggml_ctx, v, heads);
|
||||
scale = 1.f / sqrt(sqrt((float)dim_head));
|
||||
k = ggml_scale_inplace(ctx->ggml_ctx, k, scale);
|
||||
q = ggml_scale_inplace(ctx->ggml_ctx, q, scale);
|
||||
k = ggml_ext_scale(ctx->ggml_ctx, k, scale, true);
|
||||
q = ggml_ext_scale(ctx->ggml_ctx, q, scale, true);
|
||||
// auto weight = ggml_mul_mat(ctx, q, k);
|
||||
auto weight = ggml_mul_mat(ctx->ggml_ctx, k, q); // NOTE order of mul is opposite to pytorch
|
||||
|
||||
@@ -548,7 +548,7 @@ public:
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(const int n_threads,
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* id_pixel_values,
|
||||
struct ggml_tensor* prompt_embeds,
|
||||
struct ggml_tensor* id_embeds,
|
||||
@@ -561,7 +561,7 @@ public:
|
||||
};
|
||||
|
||||
// GGMLRunner::compute(get_graph, n_threads, updated_prompt_embeds);
|
||||
GGMLRunner::compute(get_graph, n_threads, true, updated_prompt_embeds, output_ctx);
|
||||
return GGMLRunner::compute(get_graph, n_threads, true, updated_prompt_embeds, output_ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define __PREPROCESSING_HPP__
|
||||
|
||||
#include "ggml_extend.hpp"
|
||||
#define M_PI_ 3.14159265358979323846
|
||||
#define M_PI_ 3.14159265358979323846f
|
||||
|
||||
void convolve(struct ggml_tensor* input, struct ggml_tensor* output, struct ggml_tensor* kernel, int padding) {
|
||||
struct ggml_init_params params;
|
||||
@@ -20,13 +20,13 @@ void convolve(struct ggml_tensor* input, struct ggml_tensor* output, struct ggml
|
||||
}
|
||||
|
||||
void gaussian_kernel(struct ggml_tensor* kernel) {
|
||||
int ks_mid = kernel->ne[0] / 2;
|
||||
int ks_mid = static_cast<int>(kernel->ne[0] / 2);
|
||||
float sigma = 1.4f;
|
||||
float normal = 1.f / (2.0f * M_PI_ * powf(sigma, 2.0f));
|
||||
for (int y = 0; y < kernel->ne[0]; y++) {
|
||||
float gx = -ks_mid + y;
|
||||
float gx = static_cast<float>(-ks_mid + y);
|
||||
for (int x = 0; x < kernel->ne[1]; x++) {
|
||||
float gy = -ks_mid + x;
|
||||
float gy = static_cast<float>(-ks_mid + x);
|
||||
float k_ = expf(-((gx * gx + gy * gy) / (2.0f * powf(sigma, 2.0f)))) * normal;
|
||||
ggml_ext_tensor_set_f32(kernel, k_, x, y);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ void grayscale(struct ggml_tensor* rgb_img, struct ggml_tensor* grayscale) {
|
||||
}
|
||||
|
||||
void prop_hypot(struct ggml_tensor* x, struct ggml_tensor* y, struct ggml_tensor* h) {
|
||||
int n_elements = ggml_nelements(h);
|
||||
int n_elements = static_cast<int>(ggml_nelements(h));
|
||||
float* dx = (float*)x->data;
|
||||
float* dy = (float*)y->data;
|
||||
float* dh = (float*)h->data;
|
||||
@@ -56,7 +56,7 @@ void prop_hypot(struct ggml_tensor* x, struct ggml_tensor* y, struct ggml_tensor
|
||||
}
|
||||
|
||||
void prop_arctan2(struct ggml_tensor* x, struct ggml_tensor* y, struct ggml_tensor* h) {
|
||||
int n_elements = ggml_nelements(h);
|
||||
int n_elements = static_cast<int>(ggml_nelements(h));
|
||||
float* dx = (float*)x->data;
|
||||
float* dy = (float*)y->data;
|
||||
float* dh = (float*)h->data;
|
||||
@@ -66,7 +66,7 @@ void prop_arctan2(struct ggml_tensor* x, struct ggml_tensor* y, struct ggml_tens
|
||||
}
|
||||
|
||||
void normalize_tensor(struct ggml_tensor* g) {
|
||||
int n_elements = ggml_nelements(g);
|
||||
int n_elements = static_cast<int>(ggml_nelements(g));
|
||||
float* dg = (float*)g->data;
|
||||
float max = -INFINITY;
|
||||
for (int i = 0; i < n_elements; i++) {
|
||||
@@ -118,7 +118,7 @@ void non_max_supression(struct ggml_tensor* result, struct ggml_tensor* G, struc
|
||||
}
|
||||
|
||||
void threshold_hystersis(struct ggml_tensor* img, float high_threshold, float low_threshold, float weak, float strong) {
|
||||
int n_elements = ggml_nelements(img);
|
||||
int n_elements = static_cast<int>(ggml_nelements(img));
|
||||
float* imd = (float*)img->data;
|
||||
float max = -INFINITY;
|
||||
for (int i = 0; i < n_elements; i++) {
|
||||
@@ -209,8 +209,8 @@ bool preprocess_canny(sd_image_t img, float high_threshold, float low_threshold,
|
||||
non_max_supression(image_gray, G, tetha);
|
||||
threshold_hystersis(image_gray, high_threshold, low_threshold, weak, strong);
|
||||
// to RGB channels
|
||||
for (int iy = 0; iy < img.height; iy++) {
|
||||
for (int ix = 0; ix < img.width; ix++) {
|
||||
for (uint32_t iy = 0; iy < img.height; iy++) {
|
||||
for (uint32_t ix = 0; ix < img.width; ix++) {
|
||||
float gray = ggml_ext_tensor_get_f32(image_gray, ix, iy);
|
||||
gray = inverse ? 1.0f - gray : gray;
|
||||
ggml_ext_tensor_set_f32(image, gray, ix, iy);
|
||||
|
||||
@@ -94,10 +94,14 @@ namespace Qwen {
|
||||
blocks["norm_added_q"] = std::shared_ptr<GGMLBlock>(new RMSNorm(dim_head, eps));
|
||||
blocks["norm_added_k"] = std::shared_ptr<GGMLBlock>(new RMSNorm(dim_head, eps));
|
||||
|
||||
float scale = 1.f / 32.f;
|
||||
float scale = 1.f / 32.f;
|
||||
bool force_prec_f32 = false;
|
||||
#ifdef SD_USE_VULKAN
|
||||
force_prec_f32 = true;
|
||||
#endif
|
||||
// The purpose of the scale here is to prevent NaN issues in certain situations.
|
||||
// For example when using CUDA but the weights are k-quants (not all prompts).
|
||||
blocks["to_out.0"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, out_dim, out_bias, false, false, scale));
|
||||
blocks["to_out.0"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, out_dim, out_bias, false, force_prec_f32, scale));
|
||||
// to_out.1 is nn.Dropout
|
||||
|
||||
blocks["to_add_out"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, out_context_dim, out_bias, false, false, scale));
|
||||
@@ -158,26 +162,25 @@ namespace Qwen {
|
||||
auto k = ggml_concat(ctx->ggml_ctx, txt_k, img_k, 2); // [N, n_txt_token + n_img_token, n_head, d_head]
|
||||
auto v = ggml_concat(ctx->ggml_ctx, txt_v, img_v, 2); // [N, n_txt_token + n_img_token, n_head, d_head]
|
||||
|
||||
auto attn = Rope::attention(ctx, q, k, v, pe, mask, (1.0f / 128.f)); // [N, n_txt_token + n_img_token, n_head*d_head]
|
||||
attn = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, attn, 0, 2, 1, 3)); // [n_txt_token + n_img_token, N, hidden_size]
|
||||
auto attn = Rope::attention(ctx, q, k, v, pe, mask, (1.0f / 128.f)); // [N, n_txt_token + n_img_token, n_head*d_head]
|
||||
auto txt_attn_out = ggml_view_3d(ctx->ggml_ctx,
|
||||
attn,
|
||||
attn->ne[0],
|
||||
attn->ne[1],
|
||||
txt->ne[1],
|
||||
attn->ne[2],
|
||||
attn->nb[1],
|
||||
attn->nb[2],
|
||||
0); // [n_txt_token, N, hidden_size]
|
||||
txt_attn_out = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, txt_attn_out, 0, 2, 1, 3)); // [N, n_txt_token, hidden_size]
|
||||
0); // [N, n_txt_token, n_head*d_head]
|
||||
auto img_attn_out = ggml_view_3d(ctx->ggml_ctx,
|
||||
attn,
|
||||
attn->ne[0],
|
||||
attn->ne[1],
|
||||
img->ne[1],
|
||||
attn->ne[2],
|
||||
attn->nb[1],
|
||||
attn->nb[2],
|
||||
attn->nb[2] * txt->ne[1]); // [n_img_token, N, hidden_size]
|
||||
img_attn_out = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, img_attn_out, 0, 2, 1, 3)); // [N, n_img_token, hidden_size]
|
||||
txt->ne[1] * attn->nb[1]); // [N, n_img_token, n_head*d_head]
|
||||
img_attn_out = ggml_cont(ctx->ggml_ctx, img_attn_out);
|
||||
txt_attn_out = ggml_cont(ctx->ggml_ctx, txt_attn_out);
|
||||
|
||||
img_attn_out = to_out_0->forward(ctx, img_attn_out);
|
||||
txt_attn_out = to_add_out->forward(ctx, txt_attn_out);
|
||||
@@ -187,11 +190,16 @@ namespace Qwen {
|
||||
};
|
||||
|
||||
class QwenImageTransformerBlock : public GGMLBlock {
|
||||
protected:
|
||||
bool zero_cond_t;
|
||||
|
||||
public:
|
||||
QwenImageTransformerBlock(int64_t dim,
|
||||
int64_t num_attention_heads,
|
||||
int64_t attention_head_dim,
|
||||
float eps = 1e-6) {
|
||||
float eps = 1e-6,
|
||||
bool zero_cond_t = false)
|
||||
: zero_cond_t(zero_cond_t) {
|
||||
// img_mod.0 is nn.SiLU()
|
||||
blocks["img_mod.1"] = std::shared_ptr<GGMLBlock>(new Linear(dim, 6 * dim, true));
|
||||
|
||||
@@ -204,7 +212,7 @@ namespace Qwen {
|
||||
|
||||
blocks["txt_norm1"] = std::shared_ptr<GGMLBlock>(new LayerNorm(dim, eps, false));
|
||||
blocks["txt_norm2"] = std::shared_ptr<GGMLBlock>(new LayerNorm(dim, eps, false));
|
||||
blocks["txt_mlp"] = std::shared_ptr<GGMLBlock>(new FeedForward(dim, dim, 4, FeedForward::Activation::GELU));
|
||||
blocks["txt_mlp"] = std::shared_ptr<GGMLBlock>(new FeedForward(dim, dim, 4, FeedForward::Activation::GELU, true));
|
||||
|
||||
blocks["attn"] = std::shared_ptr<GGMLBlock>(new QwenImageAttention(dim,
|
||||
attention_head_dim,
|
||||
@@ -216,11 +224,37 @@ namespace Qwen {
|
||||
eps));
|
||||
}
|
||||
|
||||
std::vector<ggml_tensor*> get_mod_params_vec(ggml_context* ctx, ggml_tensor* mod_params, ggml_tensor* index = nullptr) {
|
||||
// index: [N, n_img_token]
|
||||
// mod_params: [N, hidden_size * 12]
|
||||
if (index == nullptr) {
|
||||
return ggml_ext_chunk(ctx, mod_params, 6, 0);
|
||||
}
|
||||
mod_params = ggml_reshape_1d(ctx, mod_params, ggml_nelements(mod_params));
|
||||
auto mod_params_vec = ggml_ext_chunk(ctx, mod_params, 12, 0);
|
||||
index = ggml_reshape_3d(ctx, index, 1, index->ne[0], index->ne[1]); // [N, n_img_token, 1]
|
||||
index = ggml_repeat_4d(ctx, index, mod_params_vec[0]->ne[0], index->ne[1], index->ne[2], index->ne[3]); // [N, n_img_token, hidden_size]
|
||||
std::vector<ggml_tensor*> mod_results;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
auto mod_0 = mod_params_vec[i];
|
||||
auto mod_1 = mod_params_vec[i + 6];
|
||||
|
||||
// mod_result = torch.where(index == 0, mod_0, mod_1)
|
||||
// mod_result = (1 - index)*mod_0 + index*mod_1
|
||||
mod_0 = ggml_sub(ctx, ggml_repeat(ctx, mod_0, index), ggml_mul(ctx, index, mod_0)); // [N, n_img_token, hidden_size]
|
||||
mod_1 = ggml_mul(ctx, index, mod_1); // [N, n_img_token, hidden_size]
|
||||
auto mod_result = ggml_add(ctx, mod_0, mod_1);
|
||||
mod_results.push_back(mod_result);
|
||||
}
|
||||
return mod_results;
|
||||
}
|
||||
|
||||
virtual std::pair<ggml_tensor*, ggml_tensor*> forward(GGMLRunnerContext* ctx,
|
||||
struct ggml_tensor* img,
|
||||
struct ggml_tensor* txt,
|
||||
struct ggml_tensor* t_emb,
|
||||
struct ggml_tensor* pe) {
|
||||
struct ggml_tensor* pe,
|
||||
struct ggml_tensor* modulate_index = nullptr) {
|
||||
// img: [N, n_img_token, hidden_size]
|
||||
// txt: [N, n_txt_token, hidden_size]
|
||||
// pe: [n_img_token + n_txt_token, d_head/2, 2, 2]
|
||||
@@ -240,14 +274,18 @@ namespace Qwen {
|
||||
|
||||
auto img_mod_params = ggml_silu(ctx->ggml_ctx, t_emb);
|
||||
img_mod_params = img_mod_1->forward(ctx, img_mod_params);
|
||||
auto img_mod_param_vec = ggml_ext_chunk(ctx->ggml_ctx, img_mod_params, 6, 0);
|
||||
auto img_mod_param_vec = get_mod_params_vec(ctx->ggml_ctx, img_mod_params, modulate_index);
|
||||
|
||||
if (zero_cond_t) {
|
||||
t_emb = ggml_ext_chunk(ctx->ggml_ctx, t_emb, 2, 1)[0];
|
||||
}
|
||||
|
||||
auto txt_mod_params = ggml_silu(ctx->ggml_ctx, t_emb);
|
||||
txt_mod_params = txt_mod_1->forward(ctx, txt_mod_params);
|
||||
auto txt_mod_param_vec = ggml_ext_chunk(ctx->ggml_ctx, txt_mod_params, 6, 0);
|
||||
auto txt_mod_param_vec = get_mod_params_vec(ctx->ggml_ctx, txt_mod_params);
|
||||
|
||||
auto img_normed = img_norm1->forward(ctx, img);
|
||||
auto img_modulated = Flux::modulate(ctx->ggml_ctx, img_normed, img_mod_param_vec[0], img_mod_param_vec[1]);
|
||||
auto img_modulated = Flux::modulate(ctx->ggml_ctx, img_normed, img_mod_param_vec[0], img_mod_param_vec[1], modulate_index != nullptr);
|
||||
auto img_gate1 = img_mod_param_vec[2];
|
||||
|
||||
auto txt_normed = txt_norm1->forward(ctx, txt);
|
||||
@@ -260,7 +298,7 @@ namespace Qwen {
|
||||
txt = ggml_add(ctx->ggml_ctx, txt, ggml_mul(ctx->ggml_ctx, txt_attn_output, txt_gate1));
|
||||
|
||||
auto img_normed2 = img_norm2->forward(ctx, img);
|
||||
auto img_modulated2 = Flux::modulate(ctx->ggml_ctx, img_normed2, img_mod_param_vec[3], img_mod_param_vec[4]);
|
||||
auto img_modulated2 = Flux::modulate(ctx->ggml_ctx, img_normed2, img_mod_param_vec[3], img_mod_param_vec[4], modulate_index != nullptr);
|
||||
auto img_gate2 = img_mod_param_vec[5];
|
||||
|
||||
auto txt_normed2 = txt_norm2->forward(ctx, txt);
|
||||
@@ -311,16 +349,17 @@ namespace Qwen {
|
||||
};
|
||||
|
||||
struct QwenImageParams {
|
||||
int64_t patch_size = 2;
|
||||
int patch_size = 2;
|
||||
int64_t in_channels = 64;
|
||||
int64_t out_channels = 16;
|
||||
int64_t num_layers = 60;
|
||||
int num_layers = 60;
|
||||
int64_t attention_head_dim = 128;
|
||||
int64_t num_attention_heads = 24;
|
||||
int64_t joint_attention_dim = 3584;
|
||||
float theta = 10000;
|
||||
int theta = 10000;
|
||||
std::vector<int> axes_dim = {16, 56, 56};
|
||||
int64_t axes_dim_sum = 128;
|
||||
int axes_dim_sum = 128;
|
||||
bool zero_cond_t = false;
|
||||
};
|
||||
|
||||
class QwenImageModel : public GGMLBlock {
|
||||
@@ -342,7 +381,8 @@ namespace Qwen {
|
||||
auto block = std::shared_ptr<GGMLBlock>(new QwenImageTransformerBlock(inner_dim,
|
||||
params.num_attention_heads,
|
||||
params.attention_head_dim,
|
||||
1e-6f));
|
||||
1e-6f,
|
||||
params.zero_cond_t));
|
||||
blocks["transformer_blocks." + std::to_string(i)] = block;
|
||||
}
|
||||
|
||||
@@ -350,14 +390,14 @@ namespace Qwen {
|
||||
blocks["proj_out"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, params.patch_size * params.patch_size * params.out_channels));
|
||||
}
|
||||
|
||||
struct ggml_tensor* pad_to_patch_size(struct ggml_context* ctx,
|
||||
struct ggml_tensor* pad_to_patch_size(GGMLRunnerContext* ctx,
|
||||
struct ggml_tensor* x) {
|
||||
int64_t W = x->ne[0];
|
||||
int64_t H = x->ne[1];
|
||||
|
||||
int pad_h = (params.patch_size - H % params.patch_size) % params.patch_size;
|
||||
int pad_w = (params.patch_size - W % params.patch_size) % params.patch_size;
|
||||
x = ggml_pad(ctx, x, pad_w, pad_h, 0, 0); // [N, C, H + pad_h, W + pad_w]
|
||||
x = ggml_ext_pad(ctx->ggml_ctx, x, pad_w, pad_h, 0, 0, ctx->circular_x_enabled, ctx->circular_y_enabled);
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -383,10 +423,10 @@ namespace Qwen {
|
||||
return x;
|
||||
}
|
||||
|
||||
struct ggml_tensor* process_img(struct ggml_context* ctx,
|
||||
struct ggml_tensor* process_img(GGMLRunnerContext* ctx,
|
||||
struct ggml_tensor* x) {
|
||||
x = pad_to_patch_size(ctx, x);
|
||||
x = patchify(ctx, x);
|
||||
x = patchify(ctx->ggml_ctx, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -417,7 +457,8 @@ namespace Qwen {
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timestep,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* pe) {
|
||||
struct ggml_tensor* pe,
|
||||
struct ggml_tensor* modulate_index = nullptr) {
|
||||
auto time_text_embed = std::dynamic_pointer_cast<QwenTimestepProjEmbeddings>(blocks["time_text_embed"]);
|
||||
auto txt_norm = std::dynamic_pointer_cast<RMSNorm>(blocks["txt_norm"]);
|
||||
auto img_in = std::dynamic_pointer_cast<Linear>(blocks["img_in"]);
|
||||
@@ -426,18 +467,26 @@ namespace Qwen {
|
||||
auto proj_out = std::dynamic_pointer_cast<Linear>(blocks["proj_out"]);
|
||||
|
||||
auto t_emb = time_text_embed->forward(ctx, timestep);
|
||||
auto img = img_in->forward(ctx, x);
|
||||
auto txt = txt_norm->forward(ctx, context);
|
||||
txt = txt_in->forward(ctx, txt);
|
||||
if (params.zero_cond_t) {
|
||||
auto t_emb_0 = time_text_embed->forward(ctx, ggml_ext_zeros(ctx->ggml_ctx, timestep->ne[0], timestep->ne[1], timestep->ne[2], timestep->ne[3]));
|
||||
t_emb = ggml_concat(ctx->ggml_ctx, t_emb, t_emb_0, 1);
|
||||
}
|
||||
auto img = img_in->forward(ctx, x);
|
||||
auto txt = txt_norm->forward(ctx, context);
|
||||
txt = txt_in->forward(ctx, txt);
|
||||
|
||||
for (int i = 0; i < params.num_layers; i++) {
|
||||
auto block = std::dynamic_pointer_cast<QwenImageTransformerBlock>(blocks["transformer_blocks." + std::to_string(i)]);
|
||||
|
||||
auto result = block->forward(ctx, img, txt, t_emb, pe);
|
||||
auto result = block->forward(ctx, img, txt, t_emb, pe, modulate_index);
|
||||
img = result.first;
|
||||
txt = result.second;
|
||||
}
|
||||
|
||||
if (params.zero_cond_t) {
|
||||
t_emb = ggml_ext_chunk(ctx->ggml_ctx, t_emb, 2, 1)[0];
|
||||
}
|
||||
|
||||
img = norm_out->forward(ctx, img, t_emb);
|
||||
img = proj_out->forward(ctx, img);
|
||||
|
||||
@@ -449,7 +498,8 @@ namespace Qwen {
|
||||
struct ggml_tensor* timestep,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* pe,
|
||||
std::vector<ggml_tensor*> ref_latents = {}) {
|
||||
std::vector<ggml_tensor*> ref_latents = {},
|
||||
struct ggml_tensor* modulate_index = nullptr) {
|
||||
// Forward pass of DiT.
|
||||
// x: [N, C, H, W]
|
||||
// timestep: [N,]
|
||||
@@ -462,12 +512,12 @@ namespace Qwen {
|
||||
int64_t C = x->ne[2];
|
||||
int64_t N = x->ne[3];
|
||||
|
||||
auto img = process_img(ctx->ggml_ctx, x);
|
||||
uint64_t img_tokens = img->ne[1];
|
||||
auto img = process_img(ctx, x);
|
||||
int64_t img_tokens = img->ne[1];
|
||||
|
||||
if (ref_latents.size() > 0) {
|
||||
for (ggml_tensor* ref : ref_latents) {
|
||||
ref = process_img(ctx->ggml_ctx, ref);
|
||||
ref = process_img(ctx, ref);
|
||||
img = ggml_concat(ctx->ggml_ctx, img, ref, 1);
|
||||
}
|
||||
}
|
||||
@@ -475,7 +525,7 @@ namespace Qwen {
|
||||
int64_t h_len = ((H + (params.patch_size / 2)) / params.patch_size);
|
||||
int64_t w_len = ((W + (params.patch_size / 2)) / params.patch_size);
|
||||
|
||||
auto out = forward_orig(ctx, img, timestep, context, pe); // [N, h_len*w_len, ph*pw*C]
|
||||
auto out = forward_orig(ctx, img, timestep, context, pe, modulate_index); // [N, h_len*w_len, ph*pw*C]
|
||||
|
||||
if (out->ne[1] > img_tokens) {
|
||||
out = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, out, 0, 2, 1, 3)); // [num_tokens, N, C * patch_size * patch_size]
|
||||
@@ -498,19 +548,25 @@ namespace Qwen {
|
||||
QwenImageParams qwen_image_params;
|
||||
QwenImageModel qwen_image;
|
||||
std::vector<float> pe_vec;
|
||||
std::vector<float> modulate_index_vec;
|
||||
SDVersion version;
|
||||
|
||||
QwenImageRunner(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_QWEN_IMAGE)
|
||||
SDVersion version = VERSION_QWEN_IMAGE,
|
||||
bool zero_cond_t = false)
|
||||
: GGMLRunner(backend, offload_params_to_cpu) {
|
||||
qwen_image_params.num_layers = 0;
|
||||
qwen_image_params.num_layers = 0;
|
||||
qwen_image_params.zero_cond_t = zero_cond_t;
|
||||
for (auto pair : tensor_storage_map) {
|
||||
std::string tensor_name = pair.first;
|
||||
if (tensor_name.find(prefix) == std::string::npos)
|
||||
continue;
|
||||
if (tensor_name.find("__index_timestep_zero__") != std::string::npos) {
|
||||
qwen_image_params.zero_cond_t = true;
|
||||
}
|
||||
size_t pos = tensor_name.find("transformer_blocks.");
|
||||
if (pos != std::string::npos) {
|
||||
tensor_name = tensor_name.substr(pos); // remove prefix
|
||||
@@ -525,6 +581,9 @@ namespace Qwen {
|
||||
}
|
||||
}
|
||||
LOG_INFO("qwen_image_params.num_layers: %ld", qwen_image_params.num_layers);
|
||||
if (qwen_image_params.zero_cond_t) {
|
||||
LOG_INFO("use zero_cond_t");
|
||||
}
|
||||
qwen_image = QwenImageModel(qwen_image_params);
|
||||
qwen_image.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
@@ -543,7 +602,7 @@ namespace Qwen {
|
||||
std::vector<ggml_tensor*> ref_latents = {},
|
||||
bool increase_ref_index = false) {
|
||||
GGML_ASSERT(x->ne[3] == 1);
|
||||
struct ggml_cgraph* gf = ggml_new_graph_custom(compute_ctx, QWEN_IMAGE_GRAPH_SIZE, false);
|
||||
struct ggml_cgraph* gf = new_graph_custom(QWEN_IMAGE_GRAPH_SIZE);
|
||||
|
||||
x = to_backend(x);
|
||||
context = to_backend(context);
|
||||
@@ -553,16 +612,18 @@ namespace Qwen {
|
||||
ref_latents[i] = to_backend(ref_latents[i]);
|
||||
}
|
||||
|
||||
pe_vec = Rope::gen_qwen_image_pe(x->ne[1],
|
||||
x->ne[0],
|
||||
pe_vec = Rope::gen_qwen_image_pe(static_cast<int>(x->ne[1]),
|
||||
static_cast<int>(x->ne[0]),
|
||||
qwen_image_params.patch_size,
|
||||
x->ne[3],
|
||||
context->ne[1],
|
||||
static_cast<int>(x->ne[3]),
|
||||
static_cast<int>(context->ne[1]),
|
||||
ref_latents,
|
||||
increase_ref_index,
|
||||
qwen_image_params.theta,
|
||||
circular_y_enabled,
|
||||
circular_x_enabled,
|
||||
qwen_image_params.axes_dim);
|
||||
int pos_len = pe_vec.size() / qwen_image_params.axes_dim_sum / 2;
|
||||
int pos_len = static_cast<int>(pe_vec.size() / qwen_image_params.axes_dim_sum / 2);
|
||||
// LOG_DEBUG("pos_len %d", pos_len);
|
||||
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, qwen_image_params.axes_dim_sum / 2, pos_len);
|
||||
// pe->data = pe_vec.data();
|
||||
@@ -570,6 +631,31 @@ namespace Qwen {
|
||||
// pe->data = nullptr;
|
||||
set_backend_tensor_data(pe, pe_vec.data());
|
||||
|
||||
ggml_tensor* modulate_index = nullptr;
|
||||
if (qwen_image_params.zero_cond_t) {
|
||||
modulate_index_vec.clear();
|
||||
|
||||
int64_t h_len = ((x->ne[1] + (qwen_image_params.patch_size / 2)) / qwen_image_params.patch_size);
|
||||
int64_t w_len = ((x->ne[0] + (qwen_image_params.patch_size / 2)) / qwen_image_params.patch_size);
|
||||
int64_t num_img_tokens = h_len * w_len;
|
||||
|
||||
modulate_index_vec.insert(modulate_index_vec.end(), num_img_tokens, 0.f);
|
||||
int64_t num_ref_img_tokens = 0;
|
||||
for (ggml_tensor* ref : ref_latents) {
|
||||
int64_t h_len = ((ref->ne[1] + (qwen_image_params.patch_size / 2)) / qwen_image_params.patch_size);
|
||||
int64_t w_len = ((ref->ne[0] + (qwen_image_params.patch_size / 2)) / qwen_image_params.patch_size);
|
||||
|
||||
num_ref_img_tokens += h_len * w_len;
|
||||
}
|
||||
|
||||
if (num_ref_img_tokens > 0) {
|
||||
modulate_index_vec.insert(modulate_index_vec.end(), num_ref_img_tokens, 1.f);
|
||||
}
|
||||
|
||||
modulate_index = ggml_new_tensor_1d(compute_ctx, GGML_TYPE_F32, modulate_index_vec.size());
|
||||
set_backend_tensor_data(modulate_index, modulate_index_vec.data());
|
||||
}
|
||||
|
||||
auto runner_ctx = get_context();
|
||||
|
||||
struct ggml_tensor* out = qwen_image.forward(&runner_ctx,
|
||||
@@ -577,14 +663,15 @@ namespace Qwen {
|
||||
timesteps,
|
||||
context,
|
||||
pe,
|
||||
ref_latents);
|
||||
ref_latents,
|
||||
modulate_index);
|
||||
|
||||
ggml_build_forward_expand(gf, out);
|
||||
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
bool compute(int n_threads,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
@@ -599,7 +686,7 @@ namespace Qwen {
|
||||
return build_graph(x, timesteps, context, ref_latents, increase_ref_index);
|
||||
};
|
||||
|
||||
GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
return GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
|
||||
void test() {
|
||||
@@ -627,12 +714,12 @@ namespace Qwen {
|
||||
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int t0 = ggml_time_ms();
|
||||
int64_t t0 = ggml_time_ms();
|
||||
compute(8, x, timesteps, context, {}, false, &out, work_ctx);
|
||||
int t1 = ggml_time_ms();
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("qwen_image test done in %dms", t1 - t0);
|
||||
LOG_DEBUG("qwen_image test done in %lldms", t1 - t0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,4 +767,4 @@ namespace Qwen {
|
||||
|
||||
} // namespace name
|
||||
|
||||
#endif // __QWEN_IMAGE_HPP__
|
||||
#endif // __QWEN_IMAGE_HPP__
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#ifndef __RNG_MT19937_HPP__
|
||||
#define __RNG_MT19937_HPP__
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "rng.hpp"
|
||||
|
||||
// RNG imitiating torch cpu randn on CPU.
|
||||
// Port from pytorch, original license: https://github.com/pytorch/pytorch/blob/d01a7b0241ed1c4cded7e7ca097249feb343f072/LICENSE
|
||||
// Ref: https://github.com/pytorch/pytorch/blob/d01a7b0241ed1c4cded7e7ca097249feb343f072/aten/src/ATen/core/TransformationHelper.h, for uniform_real
|
||||
// Ref: https://github.com/pytorch/pytorch/blob/d01a7b0241ed1c4cded7e7ca097249feb343f072/aten/src/ATen/native/cpu/DistributionTemplates.h, for normal_kernel/normal_fill/normal_fill_16
|
||||
// Ref: https://github.com/pytorch/pytorch/blob/d01a7b0241ed1c4cded7e7ca097249feb343f072/aten/src/ATen/core/MT19937RNGEngine.h, for mt19937_engine
|
||||
// Ref: https://github.com/pytorch/pytorch/blob/d01a7b0241ed1c4cded7e7ca097249feb343f072/aten/src/ATen/core/DistributionsHelper.h, for uniform_real_distribution/normal_distribution
|
||||
class MT19937RNG : public RNG {
|
||||
static const int N = 624;
|
||||
static const int M = 397;
|
||||
static const uint32_t MATRIX_A = 0x9908b0dfU;
|
||||
static const uint32_t UMASK = 0x80000000U;
|
||||
static const uint32_t LMASK = 0x7fffffffU;
|
||||
|
||||
struct State {
|
||||
uint64_t seed_;
|
||||
int left_;
|
||||
bool seeded_;
|
||||
uint32_t next_;
|
||||
std::array<uint32_t, N> state_;
|
||||
bool has_next_gauss = false;
|
||||
double next_gauss = 0.0f;
|
||||
};
|
||||
|
||||
State s;
|
||||
|
||||
uint32_t mix_bits(uint32_t u, uint32_t v) { return (u & UMASK) | (v & LMASK); }
|
||||
uint32_t twist(uint32_t u, uint32_t v) { return (mix_bits(u, v) >> 1) ^ ((v & 1) ? MATRIX_A : 0); }
|
||||
void next_state() {
|
||||
uint32_t* p = s.state_.data();
|
||||
s.left_ = N;
|
||||
s.next_ = 0;
|
||||
for (int j = N - M + 1; --j; p++)
|
||||
p[0] = p[M] ^ twist(p[0], p[1]);
|
||||
for (int j = M; --j; p++)
|
||||
p[0] = p[M - N] ^ twist(p[0], p[1]);
|
||||
p[0] = p[M - N] ^ twist(p[0], s.state_[0]);
|
||||
}
|
||||
|
||||
uint32_t rand_uint32() {
|
||||
if (--s.left_ == 0)
|
||||
next_state();
|
||||
uint32_t y = s.state_[s.next_++];
|
||||
y ^= (y >> 11);
|
||||
y ^= (y << 7) & 0x9d2c5680U;
|
||||
y ^= (y << 15) & 0xefc60000U;
|
||||
y ^= (y >> 18);
|
||||
return y;
|
||||
}
|
||||
|
||||
uint64_t rand_uint64() {
|
||||
uint64_t high = (uint64_t)rand_uint32();
|
||||
uint64_t low = (uint64_t)rand_uint32();
|
||||
return (high << 32) | low;
|
||||
}
|
||||
|
||||
template <typename T, typename V>
|
||||
T uniform_real(V val, T from, T to) {
|
||||
constexpr auto MASK = static_cast<V>((static_cast<uint64_t>(1) << std::numeric_limits<T>::digits) - 1);
|
||||
constexpr auto DIVISOR = static_cast<T>(1) / (static_cast<uint64_t>(1) << std::numeric_limits<T>::digits);
|
||||
T x = (val & MASK) * DIVISOR;
|
||||
return (x * (to - from) + from);
|
||||
}
|
||||
|
||||
double normal_double_value(double mean, double std) {
|
||||
if (s.has_next_gauss) {
|
||||
s.has_next_gauss = false;
|
||||
return s.next_gauss;
|
||||
}
|
||||
double u1 = uniform_real(rand_uint64(), 0., 1.); // double
|
||||
double u2 = uniform_real(rand_uint64(), 0., 1.); // double
|
||||
|
||||
double r = std::sqrt(-2.0 * std::log1p(-u2));
|
||||
double theta = 2.0 * 3.14159265358979323846 * u1;
|
||||
double value = r * std::cos(theta) * std + mean;
|
||||
s.next_gauss = r * std::sin(theta) * std + mean;
|
||||
s.has_next_gauss = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
void normal_fill_16(float* data, float mean, float std) {
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
float u1 = 1.0f - data[j];
|
||||
float u2 = data[j + 8];
|
||||
float r = std::sqrt(-2.0f * std::log(u1));
|
||||
float theta = 2.0f * 3.14159265358979323846f * u2;
|
||||
data[j] = r * std::cos(theta) * std + mean;
|
||||
data[j + 8] = r * std::sin(theta) * std + mean;
|
||||
}
|
||||
}
|
||||
|
||||
void randn(float* data, int64_t size, float mean = 0.0f, float std = 1.0f) {
|
||||
if (size >= 16) {
|
||||
for (int64_t i = 0; i < size; i++) {
|
||||
data[i] = uniform_real(rand_uint32(), 0.f, 1.f);
|
||||
}
|
||||
for (int64_t i = 0; i < size - 15; i += 16) {
|
||||
normal_fill_16(data + i, mean, std);
|
||||
}
|
||||
if (size % 16 != 0) {
|
||||
// Recompute the last 16 values.
|
||||
data = data + size - 16;
|
||||
for (int64_t i = 0; i < 16; i++) {
|
||||
data[i] = uniform_real(rand_uint32(), 0.f, 1.f);
|
||||
}
|
||||
normal_fill_16(data, mean, std);
|
||||
}
|
||||
} else {
|
||||
// Strange handling, hard to understand, but keeping it consistent with PyTorch.
|
||||
for (int64_t i = 0; i < size; i++) {
|
||||
data[i] = (float)normal_double_value(mean, std);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
MT19937RNG(uint64_t seed = 0) { manual_seed(seed); }
|
||||
|
||||
void manual_seed(uint64_t seed) override {
|
||||
s.seed_ = seed;
|
||||
s.seeded_ = true;
|
||||
s.state_[0] = (uint32_t)(seed & 0xffffffffU);
|
||||
for (int j = 1; j < N; j++) {
|
||||
uint32_t prev = s.state_[j - 1];
|
||||
s.state_[j] = 1812433253U * (prev ^ (prev >> 30)) + j;
|
||||
}
|
||||
s.left_ = 1;
|
||||
s.next_ = 0;
|
||||
s.has_next_gauss = false;
|
||||
}
|
||||
|
||||
std::vector<float> randn(uint32_t n) override {
|
||||
std::vector<float> out;
|
||||
out.resize(n);
|
||||
randn((float*)out.data(), out.size());
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __RNG_MT19937_HPP__
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef __ROPE_HPP__
|
||||
#define __ROPE_HPP__
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include "ggml_extend.hpp"
|
||||
|
||||
@@ -20,11 +22,11 @@ namespace Rope {
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> transpose(const std::vector<std::vector<float>>& mat) {
|
||||
int rows = mat.size();
|
||||
int cols = mat[0].size();
|
||||
size_t rows = mat.size();
|
||||
size_t cols = mat[0].size();
|
||||
std::vector<std::vector<float>> transposed(cols, std::vector<float>(rows));
|
||||
for (int i = 0; i < rows; ++i) {
|
||||
for (int j = 0; j < cols; ++j) {
|
||||
for (size_t i = 0; i < rows; ++i) {
|
||||
for (size_t j = 0; j < cols; ++j) {
|
||||
transposed[j][i] = mat[i][j];
|
||||
}
|
||||
}
|
||||
@@ -39,7 +41,10 @@ namespace Rope {
|
||||
return flat_vec;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> rope(const std::vector<float>& pos, int dim, int theta) {
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> rope(const std::vector<float>& pos,
|
||||
int dim,
|
||||
int theta,
|
||||
const std::vector<int>& axis_wrap_dims = {}) {
|
||||
assert(dim % 2 == 0);
|
||||
int half_dim = dim / 2;
|
||||
|
||||
@@ -47,14 +52,31 @@ namespace Rope {
|
||||
|
||||
std::vector<float> omega(half_dim);
|
||||
for (int i = 0; i < half_dim; ++i) {
|
||||
omega[i] = 1.0 / std::pow(theta, scale[i]);
|
||||
omega[i] = 1.0f / ::powf(1.f * theta, scale[i]);
|
||||
}
|
||||
|
||||
int pos_size = pos.size();
|
||||
size_t pos_size = pos.size();
|
||||
std::vector<std::vector<float>> out(pos_size, std::vector<float>(half_dim));
|
||||
for (int i = 0; i < pos_size; ++i) {
|
||||
for (int j = 0; j < half_dim; ++j) {
|
||||
out[i][j] = pos[i] * omega[j];
|
||||
for (size_t i = 0; i < pos_size; ++i) {
|
||||
for (size_t j = 0; j < half_dim; ++j) {
|
||||
float angle = pos[i] * omega[j];
|
||||
if (!axis_wrap_dims.empty()) {
|
||||
size_t wrap_size = axis_wrap_dims.size();
|
||||
// mod batch size since we only store this for one item in the batch
|
||||
size_t wrap_idx = wrap_size > 0 ? (i % wrap_size) : 0;
|
||||
int wrap_dim = axis_wrap_dims[wrap_idx];
|
||||
if (wrap_dim > 0) {
|
||||
constexpr float TWO_PI = 6.28318530717958647692f;
|
||||
float cycles = omega[j] * wrap_dim / TWO_PI;
|
||||
// closest periodic harmonic, necessary to ensure things neatly tile
|
||||
// without this round, things don't tile at the boundaries and you end up
|
||||
// with the model knowing what is "center"
|
||||
float rounded = std::round(cycles);
|
||||
angle = pos[i] * TWO_PI * rounded / wrap_dim;
|
||||
}
|
||||
}
|
||||
|
||||
out[i][j] = angle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,22 +94,46 @@ namespace Rope {
|
||||
}
|
||||
|
||||
// Generate IDs for image patches and text
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_txt_ids(int bs, int context_len) {
|
||||
return std::vector<std::vector<float>>(bs * context_len, std::vector<float>(3, 0.0));
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_flux_txt_ids(int bs, int context_len, int axes_dim_num, std::set<int> arange_dims) {
|
||||
auto txt_ids = std::vector<std::vector<float>>(bs * context_len, std::vector<float>(axes_dim_num, 0.0f));
|
||||
for (int dim = 0; dim < axes_dim_num; dim++) {
|
||||
if (arange_dims.find(dim) != arange_dims.end()) {
|
||||
for (int i = 0; i < bs * context_len; i++) {
|
||||
txt_ids[i][dim] = 1.f * (i % context_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
return txt_ids;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_img_ids(int h, int w, int patch_size, int bs, int index = 0, int h_offset = 0, int w_offset = 0) {
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_flux_img_ids(int h,
|
||||
int w,
|
||||
int patch_size,
|
||||
int bs,
|
||||
int axes_dim_num,
|
||||
int index = 0,
|
||||
int h_offset = 0,
|
||||
int w_offset = 0,
|
||||
bool scale_rope = false) {
|
||||
int h_len = (h + (patch_size / 2)) / patch_size;
|
||||
int w_len = (w + (patch_size / 2)) / patch_size;
|
||||
|
||||
std::vector<std::vector<float>> img_ids(h_len * w_len, std::vector<float>(3, 0.0));
|
||||
std::vector<std::vector<float>> img_ids(h_len * w_len, std::vector<float>(axes_dim_num, 0.0));
|
||||
|
||||
std::vector<float> row_ids = linspace<float>(h_offset, h_len - 1 + h_offset, h_len);
|
||||
std::vector<float> col_ids = linspace<float>(w_offset, w_len - 1 + w_offset, w_len);
|
||||
int h_start = h_offset;
|
||||
int w_start = w_offset;
|
||||
|
||||
if (scale_rope) {
|
||||
h_start -= h_len / 2;
|
||||
w_start -= w_len / 2;
|
||||
}
|
||||
|
||||
std::vector<float> row_ids = linspace<float>(1.f * h_start, 1.f * h_start + h_len - 1, h_len);
|
||||
std::vector<float> col_ids = linspace<float>(1.f * w_start, 1.f * w_start + w_len - 1, w_len);
|
||||
|
||||
for (int i = 0; i < h_len; ++i) {
|
||||
for (int j = 0; j < w_len; ++j) {
|
||||
img_ids[i * w_len + j][0] = index;
|
||||
img_ids[i * w_len + j][0] = 1.f * index;
|
||||
img_ids[i * w_len + j][1] = row_ids[i];
|
||||
img_ids[i * w_len + j][2] = col_ids[j];
|
||||
}
|
||||
@@ -122,10 +168,11 @@ namespace Rope {
|
||||
__STATIC_INLINE__ std::vector<float> embed_nd(const std::vector<std::vector<float>>& ids,
|
||||
int bs,
|
||||
int theta,
|
||||
const std::vector<int>& axes_dim) {
|
||||
const std::vector<int>& axes_dim,
|
||||
const std::vector<std::vector<int>>& wrap_dims = {}) {
|
||||
std::vector<std::vector<float>> trans_ids = transpose(ids);
|
||||
size_t pos_len = ids.size() / bs;
|
||||
int num_axes = axes_dim.size();
|
||||
size_t num_axes = axes_dim.size();
|
||||
// for (int i = 0; i < pos_len; i++) {
|
||||
// std::cout << trans_ids[0][i] << " " << trans_ids[1][i] << " " << trans_ids[2][i] << std::endl;
|
||||
// }
|
||||
@@ -135,9 +182,14 @@ namespace Rope {
|
||||
emb_dim += d / 2;
|
||||
|
||||
std::vector<std::vector<float>> emb(bs * pos_len, std::vector<float>(emb_dim * 2 * 2, 0.0));
|
||||
int offset = 0;
|
||||
for (int i = 0; i < num_axes; ++i) {
|
||||
std::vector<std::vector<float>> rope_emb = rope(trans_ids[i], axes_dim[i], theta); // [bs*pos_len, axes_dim[i]/2 * 2 * 2]
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < num_axes; ++i) {
|
||||
std::vector<int> axis_wrap_dims;
|
||||
if (!wrap_dims.empty() && i < (int)wrap_dims.size()) {
|
||||
axis_wrap_dims = wrap_dims[i];
|
||||
}
|
||||
std::vector<std::vector<float>> rope_emb =
|
||||
rope(trans_ids[i], axes_dim[i], theta, axis_wrap_dims); // [bs*pos_len, axes_dim[i]/2 * 2 * 2]
|
||||
for (int b = 0; b < bs; ++b) {
|
||||
for (int j = 0; j < pos_len; ++j) {
|
||||
for (int k = 0; k < rope_emb[0].size(); ++k) {
|
||||
@@ -153,32 +205,44 @@ namespace Rope {
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_refs_ids(int patch_size,
|
||||
int bs,
|
||||
int axes_dim_num,
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index) {
|
||||
bool increase_ref_index,
|
||||
float ref_index_scale,
|
||||
bool scale_rope) {
|
||||
std::vector<std::vector<float>> ids;
|
||||
uint64_t curr_h_offset = 0;
|
||||
uint64_t curr_w_offset = 0;
|
||||
int index = 1;
|
||||
int curr_h_offset = 0;
|
||||
int curr_w_offset = 0;
|
||||
int index = 1;
|
||||
for (ggml_tensor* ref : ref_latents) {
|
||||
uint64_t h_offset = 0;
|
||||
uint64_t w_offset = 0;
|
||||
int h_offset = 0;
|
||||
int w_offset = 0;
|
||||
if (!increase_ref_index) {
|
||||
if (ref->ne[1] + curr_h_offset > ref->ne[0] + curr_w_offset) {
|
||||
w_offset = curr_w_offset;
|
||||
} else {
|
||||
h_offset = curr_h_offset;
|
||||
}
|
||||
scale_rope = false;
|
||||
}
|
||||
|
||||
auto ref_ids = gen_img_ids(ref->ne[1], ref->ne[0], patch_size, bs, index, h_offset, w_offset);
|
||||
auto ref_ids = gen_flux_img_ids(static_cast<int>(ref->ne[1]),
|
||||
static_cast<int>(ref->ne[0]),
|
||||
patch_size,
|
||||
bs,
|
||||
axes_dim_num,
|
||||
static_cast<int>(index * ref_index_scale),
|
||||
h_offset,
|
||||
w_offset,
|
||||
scale_rope);
|
||||
ids = concat_ids(ids, ref_ids, bs);
|
||||
|
||||
if (increase_ref_index) {
|
||||
index++;
|
||||
}
|
||||
|
||||
curr_h_offset = std::max(curr_h_offset, ref->ne[1] + h_offset);
|
||||
curr_w_offset = std::max(curr_w_offset, ref->ne[0] + w_offset);
|
||||
curr_h_offset = std::max(curr_h_offset, static_cast<int>(ref->ne[1]) + h_offset);
|
||||
curr_w_offset = std::max(curr_w_offset, static_cast<int>(ref->ne[0]) + w_offset);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
@@ -187,15 +251,18 @@ namespace Rope {
|
||||
int w,
|
||||
int patch_size,
|
||||
int bs,
|
||||
int axes_dim_num,
|
||||
int context_len,
|
||||
std::set<int> txt_arange_dims,
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index) {
|
||||
auto txt_ids = gen_txt_ids(bs, context_len);
|
||||
auto img_ids = gen_img_ids(h, w, patch_size, bs);
|
||||
bool increase_ref_index,
|
||||
float ref_index_scale) {
|
||||
auto txt_ids = gen_flux_txt_ids(bs, context_len, axes_dim_num, txt_arange_dims);
|
||||
auto img_ids = gen_flux_img_ids(h, w, patch_size, bs, axes_dim_num);
|
||||
|
||||
auto ids = concat_ids(txt_ids, img_ids, bs);
|
||||
if (ref_latents.size() > 0) {
|
||||
auto refs_ids = gen_refs_ids(patch_size, bs, ref_latents, increase_ref_index);
|
||||
auto refs_ids = gen_refs_ids(patch_size, bs, axes_dim_num, ref_latents, increase_ref_index, ref_index_scale, false);
|
||||
ids = concat_ids(ids, refs_ids, bs);
|
||||
}
|
||||
return ids;
|
||||
@@ -207,12 +274,65 @@ namespace Rope {
|
||||
int patch_size,
|
||||
int bs,
|
||||
int context_len,
|
||||
std::set<int> txt_arange_dims,
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index,
|
||||
float ref_index_scale,
|
||||
int theta,
|
||||
bool circular_h,
|
||||
bool circular_w,
|
||||
const std::vector<int>& axes_dim) {
|
||||
std::vector<std::vector<float>> ids = gen_flux_ids(h, w, patch_size, bs, context_len, ref_latents, increase_ref_index);
|
||||
return embed_nd(ids, bs, theta, axes_dim);
|
||||
std::vector<std::vector<float>> ids = gen_flux_ids(h,
|
||||
w,
|
||||
patch_size,
|
||||
bs,
|
||||
static_cast<int>(axes_dim.size()),
|
||||
context_len,
|
||||
txt_arange_dims,
|
||||
ref_latents,
|
||||
increase_ref_index,
|
||||
ref_index_scale);
|
||||
std::vector<std::vector<int>> wrap_dims;
|
||||
if ((circular_h || circular_w) && bs > 0 && axes_dim.size() >= 3) {
|
||||
int h_len = (h + (patch_size / 2)) / patch_size;
|
||||
int w_len = (w + (patch_size / 2)) / patch_size;
|
||||
if (h_len > 0 && w_len > 0) {
|
||||
size_t pos_len = ids.size() / bs;
|
||||
wrap_dims.assign(axes_dim.size(), std::vector<int>(pos_len, 0));
|
||||
size_t cursor = context_len; // text first
|
||||
const size_t img_tokens = static_cast<size_t>(h_len) * static_cast<size_t>(w_len);
|
||||
for (size_t token_i = 0; token_i < img_tokens; ++token_i) {
|
||||
if (circular_h) {
|
||||
wrap_dims[1][cursor + token_i] = h_len;
|
||||
}
|
||||
if (circular_w) {
|
||||
wrap_dims[2][cursor + token_i] = w_len;
|
||||
}
|
||||
}
|
||||
cursor += img_tokens;
|
||||
// reference latents
|
||||
for (ggml_tensor* ref : ref_latents) {
|
||||
if (ref == nullptr) {
|
||||
continue;
|
||||
}
|
||||
int ref_h = static_cast<int>(ref->ne[1]);
|
||||
int ref_w = static_cast<int>(ref->ne[0]);
|
||||
int ref_h_l = (ref_h + (patch_size / 2)) / patch_size;
|
||||
int ref_w_l = (ref_w + (patch_size / 2)) / patch_size;
|
||||
size_t ref_tokens = static_cast<size_t>(ref_h_l) * static_cast<size_t>(ref_w_l);
|
||||
for (size_t token_i = 0; token_i < ref_tokens; ++token_i) {
|
||||
if (circular_h) {
|
||||
wrap_dims[1][cursor + token_i] = ref_h_l;
|
||||
}
|
||||
if (circular_w) {
|
||||
wrap_dims[2][cursor + token_i] = ref_w_l;
|
||||
}
|
||||
}
|
||||
cursor += ref_tokens;
|
||||
}
|
||||
}
|
||||
}
|
||||
return embed_nd(ids, bs, theta, axes_dim, wrap_dims);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_qwen_image_ids(int h,
|
||||
@@ -225,17 +345,18 @@ namespace Rope {
|
||||
int h_len = (h + (patch_size / 2)) / patch_size;
|
||||
int w_len = (w + (patch_size / 2)) / patch_size;
|
||||
int txt_id_start = std::max(h_len, w_len);
|
||||
auto txt_ids = linspace<float>(txt_id_start, context_len + txt_id_start, context_len);
|
||||
auto txt_ids = linspace<float>(1.f * txt_id_start, 1.f * context_len + txt_id_start, context_len);
|
||||
std::vector<std::vector<float>> txt_ids_repeated(bs * context_len, std::vector<float>(3));
|
||||
for (int i = 0; i < bs; ++i) {
|
||||
for (int j = 0; j < txt_ids.size(); ++j) {
|
||||
txt_ids_repeated[i * txt_ids.size() + j] = {txt_ids[j], txt_ids[j], txt_ids[j]};
|
||||
}
|
||||
}
|
||||
auto img_ids = gen_img_ids(h, w, patch_size, bs);
|
||||
auto ids = concat_ids(txt_ids_repeated, img_ids, bs);
|
||||
int axes_dim_num = 3;
|
||||
auto img_ids = gen_flux_img_ids(h, w, patch_size, bs, axes_dim_num, 0, 0, 0, true);
|
||||
auto ids = concat_ids(txt_ids_repeated, img_ids, bs);
|
||||
if (ref_latents.size() > 0) {
|
||||
auto refs_ids = gen_refs_ids(patch_size, bs, ref_latents, increase_ref_index);
|
||||
auto refs_ids = gen_refs_ids(patch_size, bs, axes_dim_num, ref_latents, increase_ref_index, 1.f, true);
|
||||
ids = concat_ids(ids, refs_ids, bs);
|
||||
}
|
||||
return ids;
|
||||
@@ -250,9 +371,57 @@ namespace Rope {
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index,
|
||||
int theta,
|
||||
bool circular_h,
|
||||
bool circular_w,
|
||||
const std::vector<int>& axes_dim) {
|
||||
std::vector<std::vector<float>> ids = gen_qwen_image_ids(h, w, patch_size, bs, context_len, ref_latents, increase_ref_index);
|
||||
return embed_nd(ids, bs, theta, axes_dim);
|
||||
std::vector<std::vector<int>> wrap_dims;
|
||||
// This logic simply stores the (pad and patch_adjusted) sizes of images so we can make sure rope correctly tiles
|
||||
if ((circular_h || circular_w) && bs > 0 && axes_dim.size() >= 3) {
|
||||
int pad_h = (patch_size - (h % patch_size)) % patch_size;
|
||||
int pad_w = (patch_size - (w % patch_size)) % patch_size;
|
||||
int h_len = (h + pad_h) / patch_size;
|
||||
int w_len = (w + pad_w) / patch_size;
|
||||
if (h_len > 0 && w_len > 0) {
|
||||
const size_t total_tokens = ids.size();
|
||||
// Track per-token wrap lengths for the row/column axes so only spatial tokens become periodic.
|
||||
wrap_dims.assign(axes_dim.size(), std::vector<int>(total_tokens / bs, 0));
|
||||
size_t cursor = context_len; // ignore text tokens
|
||||
const size_t img_tokens = static_cast<size_t>(h_len) * static_cast<size_t>(w_len);
|
||||
for (size_t token_i = 0; token_i < img_tokens; ++token_i) {
|
||||
if (circular_h) {
|
||||
wrap_dims[1][cursor + token_i] = h_len;
|
||||
}
|
||||
if (circular_w) {
|
||||
wrap_dims[2][cursor + token_i] = w_len;
|
||||
}
|
||||
}
|
||||
cursor += img_tokens;
|
||||
// For each reference image, store wrap sizes as well
|
||||
for (ggml_tensor* ref : ref_latents) {
|
||||
if (ref == nullptr) {
|
||||
continue;
|
||||
}
|
||||
int ref_h = static_cast<int>(ref->ne[1]);
|
||||
int ref_w = static_cast<int>(ref->ne[0]);
|
||||
int ref_pad_h = (patch_size - (ref_h % patch_size)) % patch_size;
|
||||
int ref_pad_w = (patch_size - (ref_w % patch_size)) % patch_size;
|
||||
int ref_h_len = (ref_h + ref_pad_h) / patch_size;
|
||||
int ref_w_len = (ref_w + ref_pad_w) / patch_size;
|
||||
size_t ref_n_tokens = static_cast<size_t>(ref_h_len) * static_cast<size_t>(ref_w_len);
|
||||
for (size_t token_i = 0; token_i < ref_n_tokens; ++token_i) {
|
||||
if (circular_h) {
|
||||
wrap_dims[1][cursor + token_i] = ref_h_len;
|
||||
}
|
||||
if (circular_w) {
|
||||
wrap_dims[2][cursor + token_i] = ref_w_len;
|
||||
}
|
||||
}
|
||||
cursor += ref_n_tokens;
|
||||
}
|
||||
}
|
||||
}
|
||||
return embed_nd(ids, bs, theta, axes_dim, wrap_dims);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_vid_ids(int t,
|
||||
@@ -271,9 +440,9 @@ namespace Rope {
|
||||
|
||||
std::vector<std::vector<float>> vid_ids(t_len * h_len * w_len, std::vector<float>(3, 0.0));
|
||||
|
||||
std::vector<float> t_ids = linspace<float>(t_offset, t_len - 1 + t_offset, t_len);
|
||||
std::vector<float> h_ids = linspace<float>(h_offset, h_len - 1 + h_offset, h_len);
|
||||
std::vector<float> w_ids = linspace<float>(w_offset, w_len - 1 + w_offset, w_len);
|
||||
std::vector<float> t_ids = linspace<float>(1.f * t_offset, 1.f * t_len - 1 + t_offset, t_len);
|
||||
std::vector<float> h_ids = linspace<float>(1.f * h_offset, 1.f * h_len - 1 + h_offset, h_len);
|
||||
std::vector<float> w_ids = linspace<float>(1.f * w_offset, 1.f * w_len - 1 + w_offset, w_len);
|
||||
|
||||
for (int i = 0; i < t_len; ++i) {
|
||||
for (int j = 0; j < h_len; ++j) {
|
||||
@@ -324,8 +493,8 @@ namespace Rope {
|
||||
|
||||
GGML_ASSERT(i < grid_h * grid_w);
|
||||
|
||||
ids[i][0] = ih + iy;
|
||||
ids[i][1] = iw + ix;
|
||||
ids[i][0] = static_cast<float>(ih + iy);
|
||||
ids[i][1] = static_cast<float>(iw + ix);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
@@ -345,6 +514,79 @@ namespace Rope {
|
||||
return embed_nd(ids, 1, theta, axes_dim);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ int bound_mod(int a, int m) {
|
||||
return (m - (a % m)) % m;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_z_image_ids(int h,
|
||||
int w,
|
||||
int patch_size,
|
||||
int bs,
|
||||
int context_len,
|
||||
int seq_multi_of,
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index) {
|
||||
int padded_context_len = context_len + bound_mod(context_len, seq_multi_of);
|
||||
auto txt_ids = std::vector<std::vector<float>>(bs * padded_context_len, std::vector<float>(3, 0.0f));
|
||||
for (int i = 0; i < bs * padded_context_len; i++) {
|
||||
txt_ids[i][0] = (i % padded_context_len) + 1.f;
|
||||
}
|
||||
|
||||
int axes_dim_num = 3;
|
||||
int index = padded_context_len + 1;
|
||||
auto img_ids = gen_flux_img_ids(h, w, patch_size, bs, axes_dim_num, index);
|
||||
|
||||
int img_pad_len = bound_mod(static_cast<int>(img_ids.size() / bs), seq_multi_of);
|
||||
if (img_pad_len > 0) {
|
||||
std::vector<std::vector<float>> img_pad_ids(bs * img_pad_len, std::vector<float>(3, 0.f));
|
||||
img_ids = concat_ids(img_ids, img_pad_ids, bs);
|
||||
}
|
||||
|
||||
auto ids = concat_ids(txt_ids, img_ids, bs);
|
||||
|
||||
// ignore ref_latents for now
|
||||
return ids;
|
||||
}
|
||||
|
||||
// Generate z_image positional embeddings
|
||||
__STATIC_INLINE__ std::vector<float> gen_z_image_pe(int h,
|
||||
int w,
|
||||
int patch_size,
|
||||
int bs,
|
||||
int context_len,
|
||||
int seq_multi_of,
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index,
|
||||
int theta,
|
||||
bool circular_h,
|
||||
bool circular_w,
|
||||
const std::vector<int>& axes_dim) {
|
||||
std::vector<std::vector<float>> ids = gen_z_image_ids(h, w, patch_size, bs, context_len, seq_multi_of, ref_latents, increase_ref_index);
|
||||
std::vector<std::vector<int>> wrap_dims;
|
||||
if ((circular_h || circular_w) && bs > 0 && axes_dim.size() >= 3) {
|
||||
int pad_h = (patch_size - (h % patch_size)) % patch_size;
|
||||
int pad_w = (patch_size - (w % patch_size)) % patch_size;
|
||||
int h_len = (h + pad_h) / patch_size;
|
||||
int w_len = (w + pad_w) / patch_size;
|
||||
if (h_len > 0 && w_len > 0) {
|
||||
size_t pos_len = ids.size() / bs;
|
||||
wrap_dims.assign(axes_dim.size(), std::vector<int>(pos_len, 0));
|
||||
size_t cursor = context_len + bound_mod(context_len, seq_multi_of); // skip text (and its padding)
|
||||
size_t img_tokens = static_cast<size_t>(h_len) * static_cast<size_t>(w_len);
|
||||
for (size_t token_i = 0; token_i < img_tokens; ++token_i) {
|
||||
if (circular_h) {
|
||||
wrap_dims[1][cursor + token_i] = h_len;
|
||||
}
|
||||
if (circular_w) {
|
||||
wrap_dims[2][cursor + token_i] = w_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return embed_nd(ids, bs, theta, axes_dim, wrap_dims);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ struct ggml_tensor* apply_rope(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* pe,
|
||||
@@ -400,7 +642,7 @@ namespace Rope {
|
||||
q = apply_rope(ctx->ggml_ctx, q, pe, rope_interleaved); // [N*n_head, L, d_head]
|
||||
k = apply_rope(ctx->ggml_ctx, k, pe, rope_interleaved); // [N*n_head, L, d_head]
|
||||
|
||||
auto x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, v->ne[1], mask, false, true, ctx->flash_attn_enabled, kv_scale); // [N, L, n_head*d_head]
|
||||
auto x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, v->ne[1], mask, true, ctx->flash_attn_enabled, kv_scale); // [N, L, n_head*d_head]
|
||||
return x;
|
||||
}
|
||||
}; // namespace Rope
|
||||
|
||||
@@ -31,46 +31,50 @@ extern "C" {
|
||||
enum rng_type_t {
|
||||
STD_DEFAULT_RNG,
|
||||
CUDA_RNG,
|
||||
CPU_RNG,
|
||||
RNG_TYPE_COUNT
|
||||
};
|
||||
|
||||
enum sample_method_t {
|
||||
SAMPLE_METHOD_DEFAULT,
|
||||
EULER,
|
||||
HEUN,
|
||||
DPM2,
|
||||
DPMPP2S_A,
|
||||
DPMPP2M,
|
||||
DPMPP2Mv2,
|
||||
IPNDM,
|
||||
IPNDM_V,
|
||||
LCM,
|
||||
DDIM_TRAILING,
|
||||
TCD,
|
||||
EULER_A,
|
||||
EULER_SAMPLE_METHOD,
|
||||
EULER_A_SAMPLE_METHOD,
|
||||
HEUN_SAMPLE_METHOD,
|
||||
DPM2_SAMPLE_METHOD,
|
||||
DPMPP2S_A_SAMPLE_METHOD,
|
||||
DPMPP2M_SAMPLE_METHOD,
|
||||
DPMPP2Mv2_SAMPLE_METHOD,
|
||||
IPNDM_SAMPLE_METHOD,
|
||||
IPNDM_V_SAMPLE_METHOD,
|
||||
LCM_SAMPLE_METHOD,
|
||||
DDIM_TRAILING_SAMPLE_METHOD,
|
||||
TCD_SAMPLE_METHOD,
|
||||
RES_MULTISTEP_SAMPLE_METHOD,
|
||||
RES_2S_SAMPLE_METHOD,
|
||||
SAMPLE_METHOD_COUNT
|
||||
};
|
||||
|
||||
enum scheduler_t {
|
||||
DEFAULT,
|
||||
DISCRETE,
|
||||
KARRAS,
|
||||
EXPONENTIAL,
|
||||
AYS,
|
||||
GITS,
|
||||
SGM_UNIFORM,
|
||||
SIMPLE,
|
||||
SMOOTHSTEP,
|
||||
SCHEDULE_COUNT
|
||||
DISCRETE_SCHEDULER,
|
||||
KARRAS_SCHEDULER,
|
||||
EXPONENTIAL_SCHEDULER,
|
||||
AYS_SCHEDULER,
|
||||
GITS_SCHEDULER,
|
||||
SGM_UNIFORM_SCHEDULER,
|
||||
SIMPLE_SCHEDULER,
|
||||
SMOOTHSTEP_SCHEDULER,
|
||||
KL_OPTIMAL_SCHEDULER,
|
||||
LCM_SCHEDULER,
|
||||
BONG_TANGENT_SCHEDULER,
|
||||
SCHEDULER_COUNT
|
||||
};
|
||||
|
||||
enum prediction_t {
|
||||
DEFAULT_PRED,
|
||||
EPS_PRED,
|
||||
V_PRED,
|
||||
EDM_V_PRED,
|
||||
SD3_FLOW_PRED,
|
||||
FLOW_PRED,
|
||||
FLUX_FLOW_PRED,
|
||||
FLUX2_FLOW_PRED,
|
||||
PREDICTION_COUNT
|
||||
};
|
||||
|
||||
@@ -134,6 +138,13 @@ enum preview_t {
|
||||
PREVIEW_COUNT
|
||||
};
|
||||
|
||||
enum lora_apply_mode_t {
|
||||
LORA_APPLY_AUTO,
|
||||
LORA_APPLY_IMMEDIATELY,
|
||||
LORA_APPLY_AT_RUNTIME,
|
||||
LORA_APPLY_MODE_COUNT,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
bool enabled;
|
||||
int tile_size_x;
|
||||
@@ -143,40 +154,53 @@ typedef struct {
|
||||
float rel_size_y;
|
||||
} sd_tiling_params_t;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const char* path;
|
||||
} sd_embedding_t;
|
||||
|
||||
typedef struct {
|
||||
const char* model_path;
|
||||
const char* clip_l_path;
|
||||
const char* clip_g_path;
|
||||
const char* clip_vision_path;
|
||||
const char* t5xxl_path;
|
||||
const char* qwen2vl_path;
|
||||
const char* qwen2vl_vision_path;
|
||||
const char* llm_path;
|
||||
const char* llm_vision_path;
|
||||
const char* diffusion_model_path;
|
||||
const char* high_noise_diffusion_model_path;
|
||||
const char* vae_path;
|
||||
const char* taesd_path;
|
||||
const char* control_net_path;
|
||||
const char* lora_model_dir;
|
||||
const char* embedding_dir;
|
||||
const sd_embedding_t* embeddings;
|
||||
uint32_t embedding_count;
|
||||
const char* photo_maker_path;
|
||||
const char* tensor_type_rules;
|
||||
bool vae_decode_only;
|
||||
bool free_params_immediately;
|
||||
int n_threads;
|
||||
enum sd_type_t wtype;
|
||||
enum rng_type_t rng_type;
|
||||
enum rng_type_t sampler_rng_type;
|
||||
enum prediction_t prediction;
|
||||
enum lora_apply_mode_t lora_apply_mode;
|
||||
bool offload_params_to_cpu;
|
||||
bool enable_mmap;
|
||||
bool keep_clip_on_cpu;
|
||||
bool keep_control_net_on_cpu;
|
||||
bool keep_vae_on_cpu;
|
||||
bool flash_attn;
|
||||
bool diffusion_flash_attn;
|
||||
bool tae_preview_only;
|
||||
bool diffusion_conv_direct;
|
||||
bool vae_conv_direct;
|
||||
bool circular_x;
|
||||
bool circular_y;
|
||||
bool force_sdxl_vae_conv_scale;
|
||||
bool chroma_use_dit_mask;
|
||||
bool chroma_use_t5_mask;
|
||||
int chroma_t5_mask_pad;
|
||||
bool qwen_image_zero_cond_t;
|
||||
float flow_shift;
|
||||
} sd_ctx_params_t;
|
||||
|
||||
@@ -209,6 +233,8 @@ typedef struct {
|
||||
int sample_steps;
|
||||
float eta;
|
||||
int shifted_timestep;
|
||||
float* custom_sigmas;
|
||||
int custom_sigmas_count;
|
||||
} sd_sample_params_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -218,7 +244,44 @@ typedef struct {
|
||||
float style_strength;
|
||||
} sd_pm_params_t; // photo maker
|
||||
|
||||
enum sd_cache_mode_t {
|
||||
SD_CACHE_DISABLED = 0,
|
||||
SD_CACHE_EASYCACHE,
|
||||
SD_CACHE_UCACHE,
|
||||
SD_CACHE_DBCACHE,
|
||||
SD_CACHE_TAYLORSEER,
|
||||
SD_CACHE_CACHE_DIT,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
enum sd_cache_mode_t mode;
|
||||
float reuse_threshold;
|
||||
float start_percent;
|
||||
float end_percent;
|
||||
float error_decay_rate;
|
||||
bool use_relative_threshold;
|
||||
bool reset_error_on_compute;
|
||||
int Fn_compute_blocks;
|
||||
int Bn_compute_blocks;
|
||||
float residual_diff_threshold;
|
||||
int max_warmup_steps;
|
||||
int max_cached_steps;
|
||||
int max_continuous_cached_steps;
|
||||
int taylorseer_n_derivatives;
|
||||
int taylorseer_skip_interval;
|
||||
const char* scm_mask;
|
||||
bool scm_policy_dynamic;
|
||||
} sd_cache_params_t;
|
||||
|
||||
typedef struct {
|
||||
bool is_high_noise;
|
||||
float multiplier;
|
||||
const char* path;
|
||||
} sd_lora_t;
|
||||
|
||||
typedef struct {
|
||||
const sd_lora_t* loras;
|
||||
uint32_t lora_count;
|
||||
const char* prompt;
|
||||
const char* negative_prompt;
|
||||
int clip_skip;
|
||||
@@ -238,9 +301,12 @@ typedef struct {
|
||||
float control_strength;
|
||||
sd_pm_params_t pm_params;
|
||||
sd_tiling_params_t vae_tiling_params;
|
||||
sd_cache_params_t cache;
|
||||
} sd_img_gen_params_t;
|
||||
|
||||
typedef struct {
|
||||
const sd_lora_t* loras;
|
||||
uint32_t lora_count;
|
||||
const char* prompt;
|
||||
const char* negative_prompt;
|
||||
int clip_skip;
|
||||
@@ -257,18 +323,20 @@ typedef struct {
|
||||
int64_t seed;
|
||||
int video_frames;
|
||||
float vace_strength;
|
||||
sd_tiling_params_t vae_tiling_params;
|
||||
sd_cache_params_t cache;
|
||||
} sd_vid_gen_params_t;
|
||||
|
||||
typedef struct sd_ctx_t sd_ctx_t;
|
||||
|
||||
typedef void (*sd_log_cb_t)(enum sd_log_level_t level, const char* text, void* data);
|
||||
typedef void (*sd_progress_cb_t)(int step, int steps, float time, void* data);
|
||||
typedef void (*sd_preview_cb_t)(int step, int frame_count, sd_image_t* frames, bool is_noisy);
|
||||
typedef void (*sd_preview_cb_t)(int step, int frame_count, sd_image_t* frames, bool is_noisy, void* data);
|
||||
|
||||
SD_API void sd_set_log_callback(sd_log_cb_t sd_log_cb, void* data);
|
||||
SD_API void sd_set_progress_callback(sd_progress_cb_t cb, void* data);
|
||||
SD_API void sd_set_preview_callback(sd_preview_cb_t cb, enum preview_t mode, int interval, bool denoised, bool noisy);
|
||||
SD_API int32_t get_num_physical_cores();
|
||||
SD_API void sd_set_preview_callback(sd_preview_cb_t cb, enum preview_t mode, int interval, bool denoised, bool noisy, void* data);
|
||||
SD_API int32_t sd_get_num_physical_cores();
|
||||
SD_API const char* sd_get_system_info();
|
||||
|
||||
SD_API const char* sd_type_name(enum sd_type_t type);
|
||||
@@ -277,23 +345,29 @@ SD_API const char* sd_rng_type_name(enum rng_type_t rng_type);
|
||||
SD_API enum rng_type_t str_to_rng_type(const char* str);
|
||||
SD_API const char* sd_sample_method_name(enum sample_method_t sample_method);
|
||||
SD_API enum sample_method_t str_to_sample_method(const char* str);
|
||||
SD_API const char* sd_schedule_name(enum scheduler_t scheduler);
|
||||
SD_API enum scheduler_t str_to_schedule(const char* str);
|
||||
SD_API const char* sd_scheduler_name(enum scheduler_t scheduler);
|
||||
SD_API enum scheduler_t str_to_scheduler(const char* str);
|
||||
SD_API const char* sd_prediction_name(enum prediction_t prediction);
|
||||
SD_API enum prediction_t str_to_prediction(const char* str);
|
||||
SD_API const char* sd_preview_name(enum preview_t preview);
|
||||
SD_API enum preview_t str_to_preview(const char* str);
|
||||
SD_API const char* sd_lora_apply_mode_name(enum lora_apply_mode_t mode);
|
||||
SD_API enum lora_apply_mode_t str_to_lora_apply_mode(const char* str);
|
||||
|
||||
SD_API void sd_cache_params_init(sd_cache_params_t* cache_params);
|
||||
|
||||
SD_API void sd_ctx_params_init(sd_ctx_params_t* sd_ctx_params);
|
||||
SD_API char* sd_ctx_params_to_str(const sd_ctx_params_t* sd_ctx_params);
|
||||
|
||||
SD_API sd_ctx_t* new_sd_ctx(const sd_ctx_params_t* sd_ctx_params);
|
||||
SD_API void free_sd_ctx(sd_ctx_t* sd_ctx);
|
||||
SD_API enum sample_method_t sd_get_default_sample_method(const sd_ctx_t* sd_ctx);
|
||||
|
||||
SD_API void sd_sample_params_init(sd_sample_params_t* sample_params);
|
||||
SD_API char* sd_sample_params_to_str(const sd_sample_params_t* sample_params);
|
||||
|
||||
SD_API enum sample_method_t sd_get_default_sample_method(const sd_ctx_t* sd_ctx);
|
||||
SD_API enum scheduler_t sd_get_default_scheduler(const sd_ctx_t* sd_ctx, enum sample_method_t sample_method);
|
||||
|
||||
SD_API void sd_img_gen_params_init(sd_img_gen_params_t* sd_img_gen_params);
|
||||
SD_API char* sd_img_gen_params_to_str(const sd_img_gen_params_t* sd_img_gen_params);
|
||||
SD_API sd_image_t* generate_image(sd_ctx_t* sd_ctx, const sd_img_gen_params_t* sd_img_gen_params);
|
||||
@@ -306,7 +380,8 @@ typedef struct upscaler_ctx_t upscaler_ctx_t;
|
||||
SD_API upscaler_ctx_t* new_upscaler_ctx(const char* esrgan_path,
|
||||
bool offload_params_to_cpu,
|
||||
bool direct,
|
||||
int n_threads);
|
||||
int n_threads,
|
||||
int tile_size);
|
||||
SD_API void free_upscaler_ctx(upscaler_ctx_t* upscaler_ctx);
|
||||
|
||||
SD_API sd_image_t upscale(upscaler_ctx_t* upscaler_ctx,
|
||||
@@ -319,7 +394,8 @@ SD_API bool convert(const char* input_path,
|
||||
const char* vae_path,
|
||||
const char* output_path,
|
||||
enum sd_type_t output_type,
|
||||
const char* tensor_type_rules);
|
||||
const char* tensor_type_rules,
|
||||
bool convert_name);
|
||||
|
||||
SD_API bool preprocess_canny(sd_image_t image,
|
||||
float high_threshold,
|
||||
@@ -328,6 +404,9 @@ SD_API bool preprocess_canny(sd_image_t image,
|
||||
float strong,
|
||||
bool inverse);
|
||||
|
||||
SD_API const char* sd_commit(void);
|
||||
SD_API const char* sd_version(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -96,7 +96,7 @@ protected:
|
||||
|
||||
try {
|
||||
data = nlohmann::json::parse(json_str);
|
||||
} catch (const nlohmann::json::parse_error& e) {
|
||||
} catch (const nlohmann::json::parse_error&) {
|
||||
status_ = INVLIAD_JSON;
|
||||
return;
|
||||
}
|
||||
@@ -168,9 +168,9 @@ protected:
|
||||
kMaxTrieResultsSize);
|
||||
trie_results_size_ = 0;
|
||||
for (const auto& p : *pieces) {
|
||||
const int num_nodes = trie_->commonPrefixSearch(
|
||||
const size_t num_nodes = trie_->commonPrefixSearch(
|
||||
p.first.data(), results.data(), results.size(), p.first.size());
|
||||
trie_results_size_ = std::max(trie_results_size_, num_nodes);
|
||||
trie_results_size_ = std::max(trie_results_size_, static_cast<int>(num_nodes));
|
||||
}
|
||||
|
||||
if (trie_results_size_ == 0)
|
||||
@@ -268,7 +268,7 @@ protected:
|
||||
-1; // The starting position (in utf-8) of this node. The entire best
|
||||
// path can be constructed by backtracking along this link.
|
||||
};
|
||||
const int size = normalized.size();
|
||||
const int size = static_cast<int>(normalized.size());
|
||||
const float unk_score = min_score() - kUnkPenalty;
|
||||
// The ends are exclusive.
|
||||
std::vector<BestPathNode> best_path_ends_at(size + 1);
|
||||
@@ -281,7 +281,7 @@ protected:
|
||||
best_path_ends_at[starts_at].best_path_score;
|
||||
bool has_single_node = false;
|
||||
const int mblen =
|
||||
std::min<int>(OneCharLen(normalized.data() + starts_at),
|
||||
std::min<int>(static_cast<int>(OneCharLen(normalized.data() + starts_at)),
|
||||
size - starts_at);
|
||||
while (key_pos < size) {
|
||||
const int ret =
|
||||
@@ -302,7 +302,7 @@ protected:
|
||||
score + best_path_score_till_here;
|
||||
if (target_node.starts_at == -1 ||
|
||||
candidate_best_path_score > target_node.best_path_score) {
|
||||
target_node.best_path_score = candidate_best_path_score;
|
||||
target_node.best_path_score = static_cast<float>(candidate_best_path_score);
|
||||
target_node.starts_at = starts_at;
|
||||
target_node.id = ret;
|
||||
}
|
||||
@@ -394,7 +394,7 @@ public:
|
||||
bool padding = false) {
|
||||
if (max_length > 0 && padding) {
|
||||
size_t orig_token_num = tokens.size() - 1;
|
||||
size_t n = std::ceil(orig_token_num * 1.0 / (max_length - 1));
|
||||
size_t n = static_cast<size_t>(std::ceil(orig_token_num * 1.0 / (max_length - 1)));
|
||||
if (n == 0) {
|
||||
n = 1;
|
||||
}
|
||||
@@ -515,7 +515,7 @@ public:
|
||||
auto wi_1 = std::dynamic_pointer_cast<Linear>(blocks["wi_1"]);
|
||||
auto wo = std::dynamic_pointer_cast<Linear>(blocks["wo"]);
|
||||
|
||||
auto hidden_gelu = ggml_gelu_inplace(ctx->ggml_ctx, wi_0->forward(ctx, x));
|
||||
auto hidden_gelu = ggml_ext_gelu(ctx->ggml_ctx, wi_0->forward(ctx, x), true);
|
||||
auto hidden_linear = wi_1->forward(ctx, x);
|
||||
x = ggml_mul_inplace(ctx->ggml_ctx, hidden_gelu, hidden_linear);
|
||||
x = wo->forward(ctx, x);
|
||||
@@ -608,7 +608,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
k = ggml_scale_inplace(ctx->ggml_ctx, k, sqrt(d_head));
|
||||
k = ggml_ext_scale(ctx->ggml_ctx, k, ::sqrtf(static_cast<float>(d_head)), true);
|
||||
|
||||
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, num_heads, mask); // [N, n_token, d_head * n_head]
|
||||
|
||||
@@ -797,7 +797,7 @@ struct T5Runner : public GGMLRunner {
|
||||
input_ids = to_backend(input_ids);
|
||||
attention_mask = to_backend(attention_mask);
|
||||
|
||||
relative_position_bucket_vec = compute_relative_position_bucket(input_ids->ne[0], input_ids->ne[0]);
|
||||
relative_position_bucket_vec = compute_relative_position_bucket(static_cast<int>(input_ids->ne[0]), static_cast<int>(input_ids->ne[0]));
|
||||
|
||||
// for (int i = 0; i < relative_position_bucket_vec.size(); i++) {
|
||||
// if (i % 77 == 0) {
|
||||
@@ -820,7 +820,7 @@ struct T5Runner : public GGMLRunner {
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(const int n_threads,
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* input_ids,
|
||||
struct ggml_tensor* attention_mask,
|
||||
ggml_tensor** output,
|
||||
@@ -828,7 +828,7 @@ struct T5Runner : public GGMLRunner {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(input_ids, attention_mask);
|
||||
};
|
||||
GGMLRunner::compute(get_graph, n_threads, true, output, output_ctx);
|
||||
return GGMLRunner::compute(get_graph, n_threads, true, output, output_ctx);
|
||||
}
|
||||
|
||||
static std::vector<int> _relative_position_bucket(const std::vector<int>& relative_position,
|
||||
@@ -984,12 +984,12 @@ struct T5Embedder {
|
||||
auto attention_mask = vector_to_ggml_tensor(work_ctx, masks);
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int t0 = ggml_time_ms();
|
||||
int64_t t0 = ggml_time_ms();
|
||||
model.compute(8, input_ids, attention_mask, &out, work_ctx);
|
||||
int t1 = ggml_time_ms();
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("t5 test done in %dms", t1 - t0);
|
||||
LOG_DEBUG("t5 test done in %lldms", t1 - t0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,22 +17,43 @@ class TAEBlock : public UnaryBlock {
|
||||
protected:
|
||||
int n_in;
|
||||
int n_out;
|
||||
bool use_midblock_gn;
|
||||
|
||||
public:
|
||||
TAEBlock(int n_in, int n_out)
|
||||
: n_in(n_in), n_out(n_out) {
|
||||
TAEBlock(int n_in, int n_out, bool use_midblock_gn = false)
|
||||
: n_in(n_in), n_out(n_out), use_midblock_gn(use_midblock_gn) {
|
||||
blocks["conv.0"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_in, n_out, {3, 3}, {1, 1}, {1, 1}));
|
||||
blocks["conv.2"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_out, n_out, {3, 3}, {1, 1}, {1, 1}));
|
||||
blocks["conv.4"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_out, n_out, {3, 3}, {1, 1}, {1, 1}));
|
||||
if (n_in != n_out) {
|
||||
blocks["skip"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_in, n_out, {1, 1}, {1, 1}, {1, 1}, {1, 1}, false));
|
||||
}
|
||||
if (use_midblock_gn) {
|
||||
int n_gn = n_in * 4;
|
||||
blocks["pool.0"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_in, n_gn, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
||||
blocks["pool.1"] = std::shared_ptr<GGMLBlock>(new GroupNorm(4, n_gn));
|
||||
// pool.2 is ReLU, handled in forward
|
||||
blocks["pool.3"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_gn, n_in, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) override {
|
||||
// x: [n, n_in, h, w]
|
||||
// return: [n, n_out, h, w]
|
||||
|
||||
if (use_midblock_gn) {
|
||||
auto pool_0 = std::dynamic_pointer_cast<Conv2d>(blocks["pool.0"]);
|
||||
auto pool_1 = std::dynamic_pointer_cast<GroupNorm>(blocks["pool.1"]);
|
||||
auto pool_3 = std::dynamic_pointer_cast<Conv2d>(blocks["pool.3"]);
|
||||
|
||||
auto p = pool_0->forward(ctx, x);
|
||||
p = pool_1->forward(ctx, p);
|
||||
p = ggml_relu_inplace(ctx->ggml_ctx, p);
|
||||
p = pool_3->forward(ctx, p);
|
||||
|
||||
x = ggml_add(ctx->ggml_ctx, x, p);
|
||||
}
|
||||
|
||||
auto conv_0 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.0"]);
|
||||
auto conv_2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.2"]);
|
||||
auto conv_4 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.4"]);
|
||||
@@ -62,7 +83,7 @@ class TinyEncoder : public UnaryBlock {
|
||||
int num_blocks = 3;
|
||||
|
||||
public:
|
||||
TinyEncoder(int z_channels = 4)
|
||||
TinyEncoder(int z_channels = 4, bool use_midblock_gn = false)
|
||||
: z_channels(z_channels) {
|
||||
int index = 0;
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
@@ -80,7 +101,7 @@ public:
|
||||
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, channels, {3, 3}, {2, 2}, {1, 1}, {1, 1}, false));
|
||||
for (int i = 0; i < num_blocks; i++) {
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels));
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels, use_midblock_gn));
|
||||
}
|
||||
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, z_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
@@ -107,7 +128,7 @@ class TinyDecoder : public UnaryBlock {
|
||||
int num_blocks = 3;
|
||||
|
||||
public:
|
||||
TinyDecoder(int z_channels = 4)
|
||||
TinyDecoder(int z_channels = 4, bool use_midblock_gn = false)
|
||||
: z_channels(z_channels) {
|
||||
int index = 0;
|
||||
|
||||
@@ -115,7 +136,7 @@ public:
|
||||
index++; // nn.ReLU()
|
||||
|
||||
for (int i = 0; i < num_blocks; i++) {
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels));
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels, use_midblock_gn));
|
||||
}
|
||||
index++; // nn.Upsample()
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, channels, {3, 3}, {1, 1}, {1, 1}, {1, 1}, false));
|
||||
@@ -140,9 +161,9 @@ public:
|
||||
// z: [n, z_channels, h, w]
|
||||
// return: [n, out_channels, h*8, w*8]
|
||||
|
||||
auto h = ggml_scale(ctx->ggml_ctx, z, 1.0f / 3.0f);
|
||||
auto h = ggml_ext_scale(ctx->ggml_ctx, z, 1.0f / 3.0f);
|
||||
h = ggml_tanh_inplace(ctx->ggml_ctx, h);
|
||||
h = ggml_scale(ctx->ggml_ctx, h, 3.0f);
|
||||
h = ggml_ext_scale(ctx->ggml_ctx, h, 3.0f);
|
||||
|
||||
for (int i = 0; i < num_blocks * 3 + 10; i++) {
|
||||
if (blocks.find(std::to_string(i)) == blocks.end()) {
|
||||
@@ -162,48 +183,382 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class TPool : public UnaryBlock {
|
||||
int stride;
|
||||
|
||||
public:
|
||||
TPool(int channels, int stride)
|
||||
: stride(stride) {
|
||||
blocks["conv"] = std::shared_ptr<GGMLBlock>(new Conv2d(channels * stride, channels, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) override {
|
||||
auto conv = std::dynamic_pointer_cast<UnaryBlock>(blocks["conv"]);
|
||||
auto h = x;
|
||||
if (stride != 1) {
|
||||
h = ggml_reshape_4d(ctx->ggml_ctx, h, h->ne[0], h->ne[1], h->ne[2] * stride, h->ne[3] / stride);
|
||||
}
|
||||
h = conv->forward(ctx, h);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
class TGrow : public UnaryBlock {
|
||||
int stride;
|
||||
|
||||
public:
|
||||
TGrow(int channels, int stride)
|
||||
: stride(stride) {
|
||||
blocks["conv"] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, channels * stride, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) override {
|
||||
auto conv = std::dynamic_pointer_cast<UnaryBlock>(blocks["conv"]);
|
||||
auto h = conv->forward(ctx, x);
|
||||
if (stride != 1) {
|
||||
h = ggml_reshape_4d(ctx->ggml_ctx, h, h->ne[0], h->ne[1], h->ne[2] / stride, h->ne[3] * stride);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
class MemBlock : public GGMLBlock {
|
||||
bool has_skip_conv = false;
|
||||
|
||||
public:
|
||||
MemBlock(int channels, int out_channels)
|
||||
: has_skip_conv(channels != out_channels) {
|
||||
blocks["conv.0"] = std::shared_ptr<GGMLBlock>(new Conv2d(channels * 2, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
blocks["conv.2"] = std::shared_ptr<GGMLBlock>(new Conv2d(out_channels, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
blocks["conv.4"] = std::shared_ptr<GGMLBlock>(new Conv2d(out_channels, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
if (has_skip_conv) {
|
||||
blocks["skip"] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, out_channels, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x, struct ggml_tensor* past) {
|
||||
// x: [n, channels, h, w]
|
||||
auto conv0 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.0"]);
|
||||
auto conv1 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.2"]);
|
||||
auto conv2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.4"]);
|
||||
|
||||
auto h = ggml_concat(ctx->ggml_ctx, x, past, 2);
|
||||
h = conv0->forward(ctx, h);
|
||||
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
||||
h = conv1->forward(ctx, h);
|
||||
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
||||
h = conv2->forward(ctx, h);
|
||||
|
||||
auto skip = x;
|
||||
if (has_skip_conv) {
|
||||
auto skip_conv = std::dynamic_pointer_cast<Conv2d>(blocks["skip"]);
|
||||
skip = skip_conv->forward(ctx, x);
|
||||
}
|
||||
h = ggml_add_inplace(ctx->ggml_ctx, h, skip);
|
||||
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct ggml_tensor* patchify(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x,
|
||||
int64_t patch_size,
|
||||
int64_t b = 1) {
|
||||
// x: [f, b*c, h*q, w*r]
|
||||
// return: [f, b*c*r*q, h, w]
|
||||
if (patch_size == 1) {
|
||||
return x;
|
||||
}
|
||||
int64_t r = patch_size;
|
||||
int64_t q = patch_size;
|
||||
|
||||
int64_t W = x->ne[0];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t C = x->ne[2];
|
||||
int64_t f = x->ne[3];
|
||||
|
||||
int64_t w = W / r;
|
||||
int64_t h = H / q;
|
||||
|
||||
x = ggml_reshape_4d(ctx, x, W, q, h, C * f); // [W, q, h, C*f]
|
||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [W, h, q, C*f]
|
||||
x = ggml_reshape_4d(ctx, x, r, w, h, q * C * f); // [r, w, h, q*C*f]
|
||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 1, 2, 0, 3)); // [w, h, r, q*C*f]
|
||||
x = ggml_reshape_4d(ctx, x, w, h, r * q * C, f); // [f, b*c*r*q, h, w]
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
struct ggml_tensor* unpatchify(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x,
|
||||
int64_t patch_size,
|
||||
int64_t b = 1) {
|
||||
// x: [f, b*c*r*q, h, w]
|
||||
// return: [f, b*c, h*q, w*r]
|
||||
if (patch_size == 1) {
|
||||
return x;
|
||||
}
|
||||
int64_t r = patch_size;
|
||||
int64_t q = patch_size;
|
||||
int64_t c = x->ne[2] / b / q / r;
|
||||
int64_t f = x->ne[3];
|
||||
int64_t h = x->ne[1];
|
||||
int64_t w = x->ne[0];
|
||||
|
||||
x = ggml_reshape_4d(ctx, x, w, h, r, q * c * b * f); // [q*c*b*f, r, h, w]
|
||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 2, 0, 1, 3)); // [r, w, h, q*c*b*f]
|
||||
x = ggml_reshape_4d(ctx, x, r * w, h, q, c * b * f); // [c*b*f, q, h, r*w]
|
||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [r*w, q, h, c*b*f]
|
||||
x = ggml_reshape_4d(ctx, x, r * w, q * h, c * b, f);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
class TinyVideoEncoder : public UnaryBlock {
|
||||
int in_channels = 3;
|
||||
int hidden = 64;
|
||||
int z_channels = 4;
|
||||
int num_blocks = 3;
|
||||
int num_layers = 3;
|
||||
int patch_size = 1;
|
||||
|
||||
public:
|
||||
TinyVideoEncoder(int z_channels = 4, int patch_size = 1)
|
||||
: z_channels(z_channels), patch_size(patch_size) {
|
||||
int index = 0;
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels * patch_size * patch_size, hidden, {3, 3}, {1, 1}, {1, 1}));
|
||||
index++; // nn.ReLU()
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
int stride = i == num_layers - 1 ? 1 : 2;
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TPool(hidden, stride));
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(hidden, hidden, {3, 3}, {2, 2}, {1, 1}, {1, 1}, false));
|
||||
for (int j = 0; j < num_blocks; j++) {
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new MemBlock(hidden, hidden));
|
||||
}
|
||||
}
|
||||
blocks[std::to_string(index)] = std::shared_ptr<GGMLBlock>(new Conv2d(hidden, z_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* z) override {
|
||||
auto first_conv = std::dynamic_pointer_cast<Conv2d>(blocks["0"]);
|
||||
|
||||
if (patch_size > 1) {
|
||||
z = patchify(ctx->ggml_ctx, z, patch_size, 1);
|
||||
}
|
||||
|
||||
auto h = first_conv->forward(ctx, z);
|
||||
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
||||
|
||||
int index = 2;
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
auto pool = std::dynamic_pointer_cast<UnaryBlock>(blocks[std::to_string(index++)]);
|
||||
auto conv = std::dynamic_pointer_cast<UnaryBlock>(blocks[std::to_string(index++)]);
|
||||
|
||||
h = pool->forward(ctx, h);
|
||||
h = conv->forward(ctx, h);
|
||||
for (int j = 0; j < num_blocks; j++) {
|
||||
auto block = std::dynamic_pointer_cast<MemBlock>(blocks[std::to_string(index++)]);
|
||||
auto mem = ggml_pad_ext(ctx->ggml_ctx, h, 0, 0, 0, 0, 0, 0, 1, 0);
|
||||
mem = ggml_view_4d(ctx->ggml_ctx, mem, h->ne[0], h->ne[1], h->ne[2], h->ne[3], h->nb[1], h->nb[2], h->nb[3], 0);
|
||||
h = block->forward(ctx, h, mem);
|
||||
}
|
||||
}
|
||||
auto last_conv = std::dynamic_pointer_cast<Conv2d>(blocks[std::to_string(index)]);
|
||||
h = last_conv->forward(ctx, h);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
class TinyVideoDecoder : public UnaryBlock {
|
||||
int z_channels = 4;
|
||||
int out_channels = 3;
|
||||
int num_blocks = 3;
|
||||
static const int num_layers = 3;
|
||||
int channels[num_layers + 1] = {256, 128, 64, 64};
|
||||
int patch_size = 1;
|
||||
|
||||
public:
|
||||
TinyVideoDecoder(int z_channels = 4, int patch_size = 1)
|
||||
: z_channels(z_channels), patch_size(patch_size) {
|
||||
int index = 1; // Clamp()
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(z_channels, channels[0], {3, 3}, {1, 1}, {1, 1}));
|
||||
index++; // nn.ReLU()
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
int stride = i == 0 ? 1 : 2;
|
||||
for (int j = 0; j < num_blocks; j++) {
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new MemBlock(channels[i], channels[i]));
|
||||
}
|
||||
index++; // nn.Upsample()
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TGrow(channels[i], stride));
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels[i], channels[i + 1], {3, 3}, {1, 1}, {1, 1}, {1, 1}, false));
|
||||
}
|
||||
index++; // nn.ReLU()
|
||||
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels[num_layers], out_channels * patch_size * patch_size, {3, 3}, {1, 1}, {1, 1}));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* z) override {
|
||||
auto first_conv = std::dynamic_pointer_cast<Conv2d>(blocks["1"]);
|
||||
|
||||
// Clamp()
|
||||
auto h = ggml_ext_scale(ctx->ggml_ctx,
|
||||
ggml_tanh_inplace(ctx->ggml_ctx,
|
||||
ggml_ext_scale(ctx->ggml_ctx, z, 1.0f / 3.0f)),
|
||||
3.0f,
|
||||
true);
|
||||
|
||||
h = first_conv->forward(ctx, h);
|
||||
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
||||
int index = 3;
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
for (int j = 0; j < num_blocks; j++) {
|
||||
auto block = std::dynamic_pointer_cast<MemBlock>(blocks[std::to_string(index++)]);
|
||||
auto mem = ggml_pad_ext(ctx->ggml_ctx, h, 0, 0, 0, 0, 0, 0, 1, 0);
|
||||
mem = ggml_view_4d(ctx->ggml_ctx, mem, h->ne[0], h->ne[1], h->ne[2], h->ne[3], h->nb[1], h->nb[2], h->nb[3], 0);
|
||||
h = block->forward(ctx, h, mem);
|
||||
}
|
||||
// upsample
|
||||
index++;
|
||||
h = ggml_upscale(ctx->ggml_ctx, h, 2, GGML_SCALE_MODE_NEAREST);
|
||||
auto block = std::dynamic_pointer_cast<UnaryBlock>(blocks[std::to_string(index++)]);
|
||||
h = block->forward(ctx, h);
|
||||
block = std::dynamic_pointer_cast<UnaryBlock>(blocks[std::to_string(index++)]);
|
||||
h = block->forward(ctx, h);
|
||||
}
|
||||
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
||||
|
||||
auto last_conv = std::dynamic_pointer_cast<Conv2d>(blocks[std::to_string(++index)]);
|
||||
h = last_conv->forward(ctx, h);
|
||||
if (patch_size > 1) {
|
||||
h = unpatchify(ctx->ggml_ctx, h, patch_size, 1);
|
||||
}
|
||||
// shape(W, H, 3, 3 + T) => shape(W, H, 3, T)
|
||||
h = ggml_view_4d(ctx->ggml_ctx, h, h->ne[0], h->ne[1], h->ne[2], h->ne[3] - 3, h->nb[1], h->nb[2], h->nb[3], 3 * h->nb[3]);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
class TAEHV : public GGMLBlock {
|
||||
protected:
|
||||
bool decode_only;
|
||||
SDVersion version;
|
||||
|
||||
public:
|
||||
TAEHV(bool decode_only = true, SDVersion version = VERSION_WAN2)
|
||||
: decode_only(decode_only), version(version) {
|
||||
int z_channels = 16;
|
||||
int patch = 1;
|
||||
if (version == VERSION_WAN2_2_TI2V) {
|
||||
z_channels = 48;
|
||||
patch = 2;
|
||||
}
|
||||
blocks["decoder"] = std::shared_ptr<GGMLBlock>(new TinyVideoDecoder(z_channels, patch));
|
||||
if (!decode_only) {
|
||||
blocks["encoder"] = std::shared_ptr<GGMLBlock>(new TinyVideoEncoder(z_channels, patch));
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* decode(GGMLRunnerContext* ctx, struct ggml_tensor* z) {
|
||||
auto decoder = std::dynamic_pointer_cast<TinyVideoDecoder>(blocks["decoder"]);
|
||||
if (sd_version_is_wan(version)) {
|
||||
// (W, H, C, T) -> (W, H, T, C)
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, z, 0, 1, 3, 2));
|
||||
}
|
||||
auto result = decoder->forward(ctx, z);
|
||||
if (sd_version_is_wan(version)) {
|
||||
// (W, H, C, T) -> (W, H, T, C)
|
||||
result = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, result, 0, 1, 3, 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct ggml_tensor* encode(GGMLRunnerContext* ctx, struct ggml_tensor* x) {
|
||||
auto encoder = std::dynamic_pointer_cast<TinyVideoEncoder>(blocks["encoder"]);
|
||||
// (W, H, T, C) -> (W, H, C, T)
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 1, 3, 2));
|
||||
int64_t num_frames = x->ne[3];
|
||||
if (num_frames % 4) {
|
||||
// pad to multiple of 4 at the end
|
||||
auto last_frame = ggml_view_4d(ctx->ggml_ctx, x, x->ne[0], x->ne[1], x->ne[2], 1, x->nb[1], x->nb[2], x->nb[3], (num_frames - 1) * x->nb[3]);
|
||||
for (int i = 0; i < 4 - num_frames % 4; i++) {
|
||||
x = ggml_concat(ctx->ggml_ctx, x, last_frame, 3);
|
||||
}
|
||||
}
|
||||
x = encoder->forward(ctx, x);
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 1, 3, 2));
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
class TAESD : public GGMLBlock {
|
||||
protected:
|
||||
bool decode_only;
|
||||
bool taef2 = false;
|
||||
|
||||
public:
|
||||
TAESD(bool decode_only = true, SDVersion version = VERSION_SD1)
|
||||
: decode_only(decode_only) {
|
||||
int z_channels = 4;
|
||||
int z_channels = 4;
|
||||
bool use_midblock_gn = false;
|
||||
taef2 = sd_version_is_flux2(version);
|
||||
|
||||
if (sd_version_is_dit(version)) {
|
||||
z_channels = 16;
|
||||
}
|
||||
blocks["decoder.layers"] = std::shared_ptr<GGMLBlock>(new TinyDecoder(z_channels));
|
||||
if (taef2) {
|
||||
z_channels = 32;
|
||||
use_midblock_gn = true;
|
||||
}
|
||||
blocks["decoder.layers"] = std::shared_ptr<GGMLBlock>(new TinyDecoder(z_channels, use_midblock_gn));
|
||||
|
||||
if (!decode_only) {
|
||||
blocks["encoder.layers"] = std::shared_ptr<GGMLBlock>(new TinyEncoder(z_channels));
|
||||
blocks["encoder.layers"] = std::shared_ptr<GGMLBlock>(new TinyEncoder(z_channels, use_midblock_gn));
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* decode(GGMLRunnerContext* ctx, struct ggml_tensor* z) {
|
||||
auto decoder = std::dynamic_pointer_cast<TinyDecoder>(blocks["decoder.layers"]);
|
||||
if (taef2) {
|
||||
z = unpatchify(ctx->ggml_ctx, z, 2);
|
||||
}
|
||||
return decoder->forward(ctx, z);
|
||||
}
|
||||
|
||||
struct ggml_tensor* encode(GGMLRunnerContext* ctx, struct ggml_tensor* x) {
|
||||
auto encoder = std::dynamic_pointer_cast<TinyEncoder>(blocks["encoder.layers"]);
|
||||
return encoder->forward(ctx, x);
|
||||
auto z = encoder->forward(ctx, x);
|
||||
if (taef2) {
|
||||
z = patchify(ctx->ggml_ctx, z, 2);
|
||||
}
|
||||
return z;
|
||||
}
|
||||
};
|
||||
|
||||
struct TinyAutoEncoder : public GGMLRunner {
|
||||
TinyAutoEncoder(ggml_backend_t backend, bool offload_params_to_cpu)
|
||||
: GGMLRunner(backend, offload_params_to_cpu) {}
|
||||
virtual bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) = 0;
|
||||
|
||||
virtual bool load_from_file(const std::string& file_path, int n_threads) = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) = 0;
|
||||
};
|
||||
|
||||
struct TinyImageAutoEncoder : public TinyAutoEncoder {
|
||||
TAESD taesd;
|
||||
bool decode_only = false;
|
||||
|
||||
TinyAutoEncoder(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
bool decoder_only = true,
|
||||
SDVersion version = VERSION_SD1)
|
||||
TinyImageAutoEncoder(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
bool decoder_only = true,
|
||||
SDVersion version = VERSION_SD1)
|
||||
: decode_only(decoder_only),
|
||||
taesd(decoder_only, version),
|
||||
GGMLRunner(backend, offload_params_to_cpu) {
|
||||
TinyAutoEncoder(backend, offload_params_to_cpu) {
|
||||
taesd.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
|
||||
@@ -238,6 +593,10 @@ struct TinyAutoEncoder : public GGMLRunner {
|
||||
return success;
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) {
|
||||
taesd.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* z, bool decode_graph) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
z = to_backend(z);
|
||||
@@ -247,7 +606,7 @@ struct TinyAutoEncoder : public GGMLRunner {
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(const int n_threads,
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
@@ -256,7 +615,80 @@ struct TinyAutoEncoder : public GGMLRunner {
|
||||
return build_graph(z, decode_graph);
|
||||
};
|
||||
|
||||
GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
return GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
};
|
||||
|
||||
struct TinyVideoAutoEncoder : public TinyAutoEncoder {
|
||||
TAEHV taehv;
|
||||
bool decode_only = false;
|
||||
|
||||
TinyVideoAutoEncoder(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
bool decoder_only = true,
|
||||
SDVersion version = VERSION_WAN2)
|
||||
: decode_only(decoder_only),
|
||||
taehv(decoder_only, version),
|
||||
TinyAutoEncoder(backend, offload_params_to_cpu) {
|
||||
taehv.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
return "taehv";
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||
LOG_INFO("loading taehv from '%s', decode_only = %s", file_path.c_str(), decode_only ? "true" : "false");
|
||||
alloc_params_buffer();
|
||||
std::map<std::string, ggml_tensor*> taehv_tensors;
|
||||
taehv.get_param_tensors(taehv_tensors);
|
||||
std::set<std::string> ignore_tensors;
|
||||
if (decode_only) {
|
||||
ignore_tensors.insert("encoder.");
|
||||
}
|
||||
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file(file_path)) {
|
||||
LOG_ERROR("init taehv model loader from file failed: '%s'", file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(taehv_tensors, ignore_tensors, n_threads);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tae tensors from model loader failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("taehv model loaded");
|
||||
return success;
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) {
|
||||
taehv.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* z, bool decode_graph) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
z = to_backend(z);
|
||||
auto runner_ctx = get_context();
|
||||
struct ggml_tensor* out = decode_graph ? taehv.decode(&runner_ctx, z) : taehv.encode(&runner_ctx, z);
|
||||
ggml_build_forward_expand(gf, out);
|
||||
return gf;
|
||||
}
|
||||
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(z, decode_graph);
|
||||
};
|
||||
|
||||
return GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
- json.hpp library from: https://github.com/nlohmann/json
|
||||
- LICENSE: https://github.com/nlohmann/json/blob/develop/LICENSE.MIT
|
||||
- ZIP Library from: https://github.com/kuba--/zip
|
||||
- darts.h from: https://github.com/google/sentencepiece/tree/master/third_party/darts_clone
|
||||
LICENSE: https://github.com/kuba--/zip/blob/master/LICENSE.txt
|
||||
- darts.h from: https://github.com/google/sentencepiece/tree/master/third_party/darts_clone
|
||||
- LICENSE: https://github.com/google/sentencepiece/blob/master/third_party/darts_clone/LICENSE
|
||||
- httplib.h from: https://github.com/yhirose/cpp-httplib/blob/master/httplib.h
|
||||
- LICENSE: https://github.com/yhirose/cpp-httplib/blob/master/LICENSE
|
||||
- stb_image.h/stb_image_resize.h/stb_image_write.h from: https://github.com/nothings/stb
|
||||
- LICENSE: https://github.com/nothings/stb/blob/master/LICENSE
|
||||
@@ -845,7 +845,7 @@ inline void BitVector::build() {
|
||||
|
||||
num_ones_ = 0;
|
||||
for (std::size_t i = 0; i < units_.size(); ++i) {
|
||||
ranks_[i] = num_ones_;
|
||||
ranks_[i] = static_cast<id_type>(num_ones_);
|
||||
num_ones_ += pop_count(units_[i]);
|
||||
}
|
||||
}
|
||||
@@ -1769,7 +1769,7 @@ id_type DoubleArrayBuilder::arrange_from_keyset(const Keyset<T> &keyset,
|
||||
|
||||
inline id_type DoubleArrayBuilder::find_valid_offset(id_type id) const {
|
||||
if (extras_head_ >= units_.size()) {
|
||||
return units_.size() | (id & LOWER_MASK);
|
||||
return static_cast<id_type>(units_.size()) | (id & LOWER_MASK);
|
||||
}
|
||||
|
||||
id_type unfixed_id = extras_head_;
|
||||
@@ -1781,7 +1781,7 @@ inline id_type DoubleArrayBuilder::find_valid_offset(id_type id) const {
|
||||
unfixed_id = extras(unfixed_id).next();
|
||||
} while (unfixed_id != extras_head_);
|
||||
|
||||
return units_.size() | (id & LOWER_MASK);
|
||||
return static_cast<id_type>(units_.size()) | (id & LOWER_MASK);
|
||||
}
|
||||
|
||||
inline bool DoubleArrayBuilder::is_valid_offset(id_type id,
|
||||
@@ -1812,7 +1812,7 @@ inline void DoubleArrayBuilder::reserve_id(id_type id) {
|
||||
if (id == extras_head_) {
|
||||
extras_head_ = extras(id).next();
|
||||
if (extras_head_ == id) {
|
||||
extras_head_ = units_.size();
|
||||
extras_head_ = static_cast<id_type>(units_.size());
|
||||
}
|
||||
}
|
||||
extras(extras(id).prev()).set_next(extras(id).next());
|
||||
@@ -1821,8 +1821,8 @@ inline void DoubleArrayBuilder::reserve_id(id_type id) {
|
||||
}
|
||||
|
||||
inline void DoubleArrayBuilder::expand_units() {
|
||||
id_type src_num_units = units_.size();
|
||||
id_type src_num_blocks = num_blocks();
|
||||
id_type src_num_units = static_cast<id_type>(units_.size());
|
||||
id_type src_num_blocks = static_cast<id_type>(num_blocks());
|
||||
|
||||
id_type dest_num_units = src_num_units + BLOCK_SIZE;
|
||||
id_type dest_num_blocks = src_num_blocks + 1;
|
||||
@@ -1834,7 +1834,7 @@ inline void DoubleArrayBuilder::expand_units() {
|
||||
units_.resize(dest_num_units);
|
||||
|
||||
if (dest_num_blocks > NUM_EXTRA_BLOCKS) {
|
||||
for (std::size_t id = src_num_units; id < dest_num_units; ++id) {
|
||||
for (id_type id = src_num_units; id < dest_num_units; ++id) {
|
||||
extras(id).set_is_used(false);
|
||||
extras(id).set_is_fixed(false);
|
||||
}
|
||||
@@ -1858,9 +1858,9 @@ inline void DoubleArrayBuilder::expand_units() {
|
||||
inline void DoubleArrayBuilder::fix_all_blocks() {
|
||||
id_type begin = 0;
|
||||
if (num_blocks() > NUM_EXTRA_BLOCKS) {
|
||||
begin = num_blocks() - NUM_EXTRA_BLOCKS;
|
||||
begin = static_cast<id_type>(num_blocks() - NUM_EXTRA_BLOCKS);
|
||||
}
|
||||
id_type end = num_blocks();
|
||||
id_type end = static_cast<id_type>(num_blocks());
|
||||
|
||||
for (id_type block_id = begin; block_id != end; ++block_id) {
|
||||
fix_block(block_id);
|
||||
|
||||
@@ -257,6 +257,10 @@ int stbi_write_tga_with_rle = 1;
|
||||
int stbi_write_force_png_filter = -1;
|
||||
#endif
|
||||
|
||||
#ifndef STBMIN
|
||||
#define STBMIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif // STBMIN
|
||||
|
||||
static int stbi__flip_vertically_on_write = 0;
|
||||
|
||||
STBIWDEF void stbi_flip_vertically_on_write(int flag)
|
||||
@@ -1179,8 +1183,8 @@ STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int s
|
||||
if (!zlib) return 0;
|
||||
|
||||
if(parameters != NULL) {
|
||||
param_length = strlen(parameters);
|
||||
param_length += strlen("parameters") + 1; // For the name and the null-byte
|
||||
param_length = (int)strlen(parameters);
|
||||
param_length += (int)strlen("parameters") + 1; // For the name and the null-byte
|
||||
}
|
||||
|
||||
// each tag requires 12 bytes of overhead
|
||||
@@ -1526,11 +1530,11 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in
|
||||
if(parameters != NULL) {
|
||||
stbiw__putc(s, 0xFF /* comnent */ );
|
||||
stbiw__putc(s, 0xFE /* marker */ );
|
||||
size_t param_length = std::min(2 + strlen("parameters") + 1 + strlen(parameters) + 1, (size_t) 0xFFFF);
|
||||
int param_length = STBMIN(2 + (int)strlen("parameters") + 1 + (int)strlen(parameters) + 1, 0xFFFF);
|
||||
stbiw__putc(s, param_length >> 8); // no need to mask, length < 65536
|
||||
stbiw__putc(s, param_length & 0xFF);
|
||||
s->func(s->context, (void*)"parameters", strlen("parameters") + 1); // std::string is zero-terminated
|
||||
s->func(s->context, (void*)parameters, std::min(param_length, (size_t) 65534) - 2 - strlen("parameters") - 1);
|
||||
s->func(s->context, (void*)"parameters", (int)strlen("parameters") + 1); // std::string is zero-terminated
|
||||
s->func(s->context, (void*)parameters, STBMIN(param_length, 65534) - 2 - (int)strlen("parameters") - 1);
|
||||
if(param_length > 65534) stbiw__putc(s, 0); // always zero-terminate for safety
|
||||
if(param_length & 1) stbiw__putc(s, 0xFF); // pad to even length
|
||||
}
|
||||
|
||||
@@ -811,6 +811,8 @@ bool starts_with(const std::vector<char32_t>& text,
|
||||
return std::equal(prefix.begin(), prefix.end(), text.begin() + index);
|
||||
}
|
||||
|
||||
// mistral: [^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]*[\p{Ll}\p{Lm}\p{Lo}\p{M}]+|[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]+[\p{Ll}\p{Lm}\p{Lo}\p{M}]*|\p{N}| ?[^\s\p{L}\p{N}]+[\r\n/]*|\s*[\r\n]+|\s+(?!\S)|\s+
|
||||
// qwen2: (?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}| ?[^\s\p{L}\p{N}]+[\r\n]*|\s*[\r\n]+|\s+(?!\S)|\s+
|
||||
std::vector<std::string> token_split(const std::string& text) {
|
||||
std::vector<std::string> tokens;
|
||||
auto cps = utf8_to_codepoints(text);
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
#ifndef __UCACHE_HPP__
|
||||
#define __UCACHE_HPP__
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "denoiser.hpp"
|
||||
#include "ggml_extend.hpp"
|
||||
|
||||
struct UCacheConfig {
|
||||
bool enabled = false;
|
||||
float reuse_threshold = 1.0f;
|
||||
float start_percent = 0.15f;
|
||||
float end_percent = 0.95f;
|
||||
float error_decay_rate = 1.0f;
|
||||
bool use_relative_threshold = true;
|
||||
bool adaptive_threshold = true;
|
||||
float early_step_multiplier = 0.5f;
|
||||
float late_step_multiplier = 1.5f;
|
||||
bool reset_error_on_compute = true;
|
||||
};
|
||||
|
||||
struct UCacheCacheEntry {
|
||||
std::vector<float> diff;
|
||||
};
|
||||
|
||||
struct UCacheState {
|
||||
UCacheConfig config;
|
||||
Denoiser* denoiser = nullptr;
|
||||
float start_sigma = std::numeric_limits<float>::max();
|
||||
float end_sigma = 0.0f;
|
||||
bool initialized = false;
|
||||
bool initial_step = true;
|
||||
bool skip_current_step = false;
|
||||
bool step_active = false;
|
||||
const SDCondition* anchor_condition = nullptr;
|
||||
std::unordered_map<const SDCondition*, UCacheCacheEntry> cache_diffs;
|
||||
std::vector<float> prev_input;
|
||||
std::vector<float> prev_output;
|
||||
float output_prev_norm = 0.0f;
|
||||
bool has_prev_input = false;
|
||||
bool has_prev_output = false;
|
||||
bool has_output_prev_norm = false;
|
||||
bool has_relative_transformation_rate = false;
|
||||
float relative_transformation_rate = 0.0f;
|
||||
float cumulative_change_rate = 0.0f;
|
||||
float last_input_change = 0.0f;
|
||||
bool has_last_input_change = false;
|
||||
int total_steps_skipped = 0;
|
||||
int current_step_index = -1;
|
||||
int steps_computed_since_active = 0;
|
||||
float accumulated_error = 0.0f;
|
||||
float reference_output_norm = 0.0f;
|
||||
|
||||
struct BlockMetrics {
|
||||
float sum_transformation_rate = 0.0f;
|
||||
float sum_output_norm = 0.0f;
|
||||
int sample_count = 0;
|
||||
float min_change_rate = std::numeric_limits<float>::max();
|
||||
float max_change_rate = 0.0f;
|
||||
|
||||
void reset() {
|
||||
sum_transformation_rate = 0.0f;
|
||||
sum_output_norm = 0.0f;
|
||||
sample_count = 0;
|
||||
min_change_rate = std::numeric_limits<float>::max();
|
||||
max_change_rate = 0.0f;
|
||||
}
|
||||
|
||||
void record(float change_rate, float output_norm) {
|
||||
if (std::isfinite(change_rate) && change_rate > 0.0f) {
|
||||
sum_transformation_rate += change_rate;
|
||||
sum_output_norm += output_norm;
|
||||
sample_count++;
|
||||
if (change_rate < min_change_rate)
|
||||
min_change_rate = change_rate;
|
||||
if (change_rate > max_change_rate)
|
||||
max_change_rate = change_rate;
|
||||
}
|
||||
}
|
||||
|
||||
float avg_transformation_rate() const {
|
||||
return (sample_count > 0) ? (sum_transformation_rate / sample_count) : 0.0f;
|
||||
}
|
||||
|
||||
float avg_output_norm() const {
|
||||
return (sample_count > 0) ? (sum_output_norm / sample_count) : 0.0f;
|
||||
}
|
||||
};
|
||||
BlockMetrics block_metrics;
|
||||
int total_active_steps = 0;
|
||||
|
||||
void reset_runtime() {
|
||||
initial_step = true;
|
||||
skip_current_step = false;
|
||||
step_active = false;
|
||||
anchor_condition = nullptr;
|
||||
cache_diffs.clear();
|
||||
prev_input.clear();
|
||||
prev_output.clear();
|
||||
output_prev_norm = 0.0f;
|
||||
has_prev_input = false;
|
||||
has_prev_output = false;
|
||||
has_output_prev_norm = false;
|
||||
has_relative_transformation_rate = false;
|
||||
relative_transformation_rate = 0.0f;
|
||||
cumulative_change_rate = 0.0f;
|
||||
last_input_change = 0.0f;
|
||||
has_last_input_change = false;
|
||||
total_steps_skipped = 0;
|
||||
current_step_index = -1;
|
||||
steps_computed_since_active = 0;
|
||||
accumulated_error = 0.0f;
|
||||
reference_output_norm = 0.0f;
|
||||
block_metrics.reset();
|
||||
total_active_steps = 0;
|
||||
}
|
||||
|
||||
void init(const UCacheConfig& cfg, Denoiser* d) {
|
||||
config = cfg;
|
||||
denoiser = d;
|
||||
initialized = cfg.enabled && d != nullptr;
|
||||
reset_runtime();
|
||||
if (initialized) {
|
||||
start_sigma = percent_to_sigma(config.start_percent);
|
||||
end_sigma = percent_to_sigma(config.end_percent);
|
||||
}
|
||||
}
|
||||
|
||||
void set_sigmas(const std::vector<float>& sigmas) {
|
||||
if (!initialized || sigmas.size() < 2) {
|
||||
return;
|
||||
}
|
||||
size_t n_steps = sigmas.size() - 1;
|
||||
|
||||
size_t start_step = static_cast<size_t>(config.start_percent * n_steps);
|
||||
size_t end_step = static_cast<size_t>(config.end_percent * n_steps);
|
||||
|
||||
if (start_step >= n_steps)
|
||||
start_step = n_steps - 1;
|
||||
if (end_step >= n_steps)
|
||||
end_step = n_steps - 1;
|
||||
|
||||
start_sigma = sigmas[start_step];
|
||||
end_sigma = sigmas[end_step];
|
||||
|
||||
if (start_sigma < end_sigma) {
|
||||
std::swap(start_sigma, end_sigma);
|
||||
}
|
||||
}
|
||||
|
||||
bool enabled() const {
|
||||
return initialized && config.enabled;
|
||||
}
|
||||
|
||||
float percent_to_sigma(float percent) const {
|
||||
if (!denoiser) {
|
||||
return 0.0f;
|
||||
}
|
||||
if (percent <= 0.0f) {
|
||||
return std::numeric_limits<float>::max();
|
||||
}
|
||||
if (percent >= 1.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
float t = (1.0f - percent) * (TIMESTEPS - 1);
|
||||
return denoiser->t_to_sigma(t);
|
||||
}
|
||||
|
||||
void begin_step(int step_index, float sigma) {
|
||||
if (!enabled()) {
|
||||
return;
|
||||
}
|
||||
if (step_index == current_step_index) {
|
||||
return;
|
||||
}
|
||||
current_step_index = step_index;
|
||||
skip_current_step = false;
|
||||
has_last_input_change = false;
|
||||
step_active = false;
|
||||
|
||||
if (sigma > start_sigma) {
|
||||
return;
|
||||
}
|
||||
if (!(sigma > end_sigma)) {
|
||||
return;
|
||||
}
|
||||
step_active = true;
|
||||
total_active_steps++;
|
||||
}
|
||||
|
||||
bool step_is_active() const {
|
||||
return enabled() && step_active;
|
||||
}
|
||||
|
||||
bool is_step_skipped() const {
|
||||
return enabled() && step_active && skip_current_step;
|
||||
}
|
||||
|
||||
float get_adaptive_threshold(int estimated_total_steps = 0) const {
|
||||
float base_threshold = config.reuse_threshold;
|
||||
|
||||
if (!config.adaptive_threshold) {
|
||||
return base_threshold;
|
||||
}
|
||||
|
||||
int effective_total = estimated_total_steps;
|
||||
if (effective_total <= 0) {
|
||||
effective_total = std::max(20, steps_computed_since_active * 2);
|
||||
}
|
||||
|
||||
float progress = (effective_total > 0) ? (static_cast<float>(steps_computed_since_active) / effective_total) : 0.0f;
|
||||
|
||||
float multiplier = 1.0f;
|
||||
if (progress < 0.2f) {
|
||||
multiplier = config.early_step_multiplier;
|
||||
} else if (progress > 0.8f) {
|
||||
multiplier = config.late_step_multiplier;
|
||||
}
|
||||
|
||||
return base_threshold * multiplier;
|
||||
}
|
||||
|
||||
bool has_cache(const SDCondition* cond) const {
|
||||
auto it = cache_diffs.find(cond);
|
||||
return it != cache_diffs.end() && !it->second.diff.empty();
|
||||
}
|
||||
|
||||
void update_cache(const SDCondition* cond, ggml_tensor* input, ggml_tensor* output) {
|
||||
UCacheCacheEntry& entry = cache_diffs[cond];
|
||||
size_t ne = static_cast<size_t>(ggml_nelements(output));
|
||||
entry.diff.resize(ne);
|
||||
float* out_data = (float*)output->data;
|
||||
float* in_data = (float*)input->data;
|
||||
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
entry.diff[i] = out_data[i] - in_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
void apply_cache(const SDCondition* cond, ggml_tensor* input, ggml_tensor* output) {
|
||||
auto it = cache_diffs.find(cond);
|
||||
if (it == cache_diffs.end() || it->second.diff.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy_ggml_tensor(output, input);
|
||||
float* out_data = (float*)output->data;
|
||||
const std::vector<float>& diff = it->second.diff;
|
||||
for (size_t i = 0; i < diff.size(); ++i) {
|
||||
out_data[i] += diff[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool before_condition(const SDCondition* cond,
|
||||
ggml_tensor* input,
|
||||
ggml_tensor* output,
|
||||
float sigma,
|
||||
int step_index) {
|
||||
if (!enabled() || step_index < 0) {
|
||||
return false;
|
||||
}
|
||||
if (step_index != current_step_index) {
|
||||
begin_step(step_index, sigma);
|
||||
}
|
||||
if (!step_active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (initial_step) {
|
||||
anchor_condition = cond;
|
||||
initial_step = false;
|
||||
}
|
||||
|
||||
bool is_anchor = (cond == anchor_condition);
|
||||
|
||||
if (skip_current_step) {
|
||||
if (has_cache(cond)) {
|
||||
apply_cache(cond, input, output);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_anchor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!has_prev_input || !has_prev_output || !has_cache(cond)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t ne = static_cast<size_t>(ggml_nelements(input));
|
||||
if (prev_input.size() != ne) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float* input_data = (float*)input->data;
|
||||
last_input_change = 0.0f;
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
last_input_change += std::fabs(input_data[i] - prev_input[i]);
|
||||
}
|
||||
if (ne > 0) {
|
||||
last_input_change /= static_cast<float>(ne);
|
||||
}
|
||||
has_last_input_change = true;
|
||||
|
||||
if (has_output_prev_norm && has_relative_transformation_rate &&
|
||||
last_input_change > 0.0f && output_prev_norm > 0.0f) {
|
||||
float approx_output_change_rate = (relative_transformation_rate * last_input_change) / output_prev_norm;
|
||||
accumulated_error = accumulated_error * config.error_decay_rate + approx_output_change_rate;
|
||||
|
||||
float effective_threshold = get_adaptive_threshold();
|
||||
if (config.use_relative_threshold && reference_output_norm > 0.0f) {
|
||||
effective_threshold = effective_threshold * reference_output_norm;
|
||||
}
|
||||
|
||||
if (accumulated_error < effective_threshold) {
|
||||
skip_current_step = true;
|
||||
total_steps_skipped++;
|
||||
apply_cache(cond, input, output);
|
||||
return true;
|
||||
} else if (config.reset_error_on_compute) {
|
||||
accumulated_error = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void after_condition(const SDCondition* cond, ggml_tensor* input, ggml_tensor* output) {
|
||||
if (!step_is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_cache(cond, input, output);
|
||||
|
||||
if (cond != anchor_condition) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t ne = static_cast<size_t>(ggml_nelements(input));
|
||||
float* in_data = (float*)input->data;
|
||||
prev_input.resize(ne);
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
prev_input[i] = in_data[i];
|
||||
}
|
||||
has_prev_input = true;
|
||||
|
||||
float* out_data = (float*)output->data;
|
||||
float output_change = 0.0f;
|
||||
if (has_prev_output && prev_output.size() == ne) {
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
output_change += std::fabs(out_data[i] - prev_output[i]);
|
||||
}
|
||||
if (ne > 0) {
|
||||
output_change /= static_cast<float>(ne);
|
||||
}
|
||||
}
|
||||
|
||||
prev_output.resize(ne);
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
prev_output[i] = out_data[i];
|
||||
}
|
||||
has_prev_output = true;
|
||||
|
||||
float mean_abs = 0.0f;
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
mean_abs += std::fabs(out_data[i]);
|
||||
}
|
||||
output_prev_norm = (ne > 0) ? (mean_abs / static_cast<float>(ne)) : 0.0f;
|
||||
has_output_prev_norm = output_prev_norm > 0.0f;
|
||||
|
||||
if (reference_output_norm == 0.0f) {
|
||||
reference_output_norm = output_prev_norm;
|
||||
}
|
||||
|
||||
if (has_last_input_change && last_input_change > 0.0f && output_change > 0.0f) {
|
||||
float rate = output_change / last_input_change;
|
||||
if (std::isfinite(rate)) {
|
||||
relative_transformation_rate = rate;
|
||||
has_relative_transformation_rate = true;
|
||||
block_metrics.record(rate, output_prev_norm);
|
||||
}
|
||||
}
|
||||
|
||||
has_last_input_change = false;
|
||||
}
|
||||
|
||||
void log_block_metrics() const {
|
||||
if (block_metrics.sample_count > 0) {
|
||||
LOG_INFO("UCacheBlockMetrics: samples=%d, avg_rate=%.4f, min=%.4f, max=%.4f, avg_norm=%.4f",
|
||||
block_metrics.sample_count,
|
||||
block_metrics.avg_transformation_rate(),
|
||||
block_metrics.min_change_rate,
|
||||
block_metrics.max_change_rate,
|
||||
block_metrics.avg_output_norm());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __UCACHE_HPP__
|
||||
@@ -7,12 +7,12 @@
|
||||
|
||||
/*==================================================== UnetModel =====================================================*/
|
||||
|
||||
#define UNET_GRAPH_SIZE 10240
|
||||
#define UNET_GRAPH_SIZE 102400
|
||||
|
||||
class SpatialVideoTransformer : public SpatialTransformer {
|
||||
protected:
|
||||
int64_t time_depth;
|
||||
int64_t max_time_embed_period;
|
||||
int max_time_embed_period;
|
||||
|
||||
public:
|
||||
SpatialVideoTransformer(int64_t in_channels,
|
||||
@@ -21,8 +21,8 @@ public:
|
||||
int64_t depth,
|
||||
int64_t context_dim,
|
||||
bool use_linear,
|
||||
int64_t time_depth = 1,
|
||||
int64_t max_time_embed_period = 10000)
|
||||
int64_t time_depth = 1,
|
||||
int max_time_embed_period = 10000)
|
||||
: SpatialTransformer(in_channels, n_head, d_head, depth, context_dim, use_linear),
|
||||
max_time_embed_period(max_time_embed_period) {
|
||||
// We will convert unet transformer linear to conv2d 1x1 when loading the weights, so use_linear is always False
|
||||
@@ -112,9 +112,9 @@ public:
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 1, 2, 0, 3)); // [N, h, w, inner_dim]
|
||||
x = ggml_reshape_3d(ctx->ggml_ctx, x, inner_dim, w * h, n); // [N, h * w, inner_dim]
|
||||
|
||||
auto num_frames = ggml_arange(ctx->ggml_ctx, 0, timesteps, 1);
|
||||
auto num_frames = ggml_arange(ctx->ggml_ctx, 0.f, static_cast<float>(timesteps), 1.f);
|
||||
// since b is 1, no need to do repeat
|
||||
auto t_emb = ggml_ext_timestep_embedding(ctx->ggml_ctx, num_frames, in_channels, max_time_embed_period); // [N, in_channels]
|
||||
auto t_emb = ggml_ext_timestep_embedding(ctx->ggml_ctx, num_frames, static_cast<int>(in_channels), max_time_embed_period); // [N, in_channels]
|
||||
|
||||
auto emb = time_pos_embed_0->forward(ctx, t_emb);
|
||||
emb = ggml_silu_inplace(ctx->ggml_ctx, emb);
|
||||
@@ -201,6 +201,9 @@ public:
|
||||
num_head_channels = 64;
|
||||
num_heads = -1;
|
||||
use_linear_projection = true;
|
||||
if (version == VERSION_SDXL_VEGA) {
|
||||
transformer_depth = {1, 1, 2};
|
||||
}
|
||||
} else if (version == VERSION_SVD) {
|
||||
in_channels = 8;
|
||||
out_channels = 4;
|
||||
@@ -215,10 +218,13 @@ public:
|
||||
} else if (sd_version_is_unet_edit(version)) {
|
||||
in_channels = 8;
|
||||
}
|
||||
if (version == VERSION_SD1_TINY_UNET || version == VERSION_SD2_TINY_UNET) {
|
||||
if (version == VERSION_SD1_TINY_UNET || version == VERSION_SD2_TINY_UNET || version == VERSION_SDXS) {
|
||||
num_res_blocks = 1;
|
||||
channel_mult = {1, 2, 4};
|
||||
tiny_unet = true;
|
||||
if (version == VERSION_SDXS) {
|
||||
attention_resolutions = {4, 2}; // here just like SDXL
|
||||
}
|
||||
}
|
||||
|
||||
// dims is always 2
|
||||
@@ -316,7 +322,7 @@ public:
|
||||
}
|
||||
if (!tiny_unet) {
|
||||
blocks["middle_block.0"] = std::shared_ptr<GGMLBlock>(get_resblock(ch, time_embed_dim, ch));
|
||||
if (version != VERSION_SDXL_SSD1B) {
|
||||
if (version != VERSION_SDXL_SSD1B && version != VERSION_SDXL_VEGA) {
|
||||
blocks["middle_block.1"] = std::shared_ptr<GGMLBlock>(get_attention_layer(ch,
|
||||
n_head,
|
||||
d_head,
|
||||
@@ -517,16 +523,16 @@ public:
|
||||
// middle_block
|
||||
if (!tiny_unet) {
|
||||
h = resblock_forward("middle_block.0", ctx, h, emb, num_video_frames); // [N, 4*model_channels, h/8, w/8]
|
||||
if (version != VERSION_SDXL_SSD1B) {
|
||||
if (version != VERSION_SDXL_SSD1B && version != VERSION_SDXL_VEGA) {
|
||||
h = attention_layer_forward("middle_block.1", ctx, h, context, num_video_frames); // [N, 4*model_channels, h/8, w/8]
|
||||
h = resblock_forward("middle_block.2", ctx, h, emb, num_video_frames); // [N, 4*model_channels, h/8, w/8]
|
||||
}
|
||||
}
|
||||
if (controls.size() > 0) {
|
||||
auto cs = ggml_scale_inplace(ctx->ggml_ctx, controls[controls.size() - 1], control_strength);
|
||||
auto cs = ggml_ext_scale(ctx->ggml_ctx, controls[controls.size() - 1], control_strength, true);
|
||||
h = ggml_add(ctx->ggml_ctx, h, cs); // middle control
|
||||
}
|
||||
int control_offset = controls.size() - 2;
|
||||
int control_offset = static_cast<int>(controls.size() - 2);
|
||||
|
||||
// output_blocks
|
||||
int output_block_idx = 0;
|
||||
@@ -536,7 +542,7 @@ public:
|
||||
hs.pop_back();
|
||||
|
||||
if (controls.size() > 0) {
|
||||
auto cs = ggml_scale_inplace(ctx->ggml_ctx, controls[control_offset], control_strength);
|
||||
auto cs = ggml_ext_scale(ctx->ggml_ctx, controls[control_offset], control_strength, true);
|
||||
h_skip = ggml_add(ctx->ggml_ctx, h_skip, cs); // control net condition
|
||||
control_offset--;
|
||||
}
|
||||
@@ -612,10 +618,10 @@ struct UNetModelRunner : public GGMLRunner {
|
||||
int num_video_frames = -1,
|
||||
std::vector<struct ggml_tensor*> controls = {},
|
||||
float control_strength = 0.f) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph_custom(compute_ctx, UNET_GRAPH_SIZE, false);
|
||||
struct ggml_cgraph* gf = new_graph_custom(UNET_GRAPH_SIZE);
|
||||
|
||||
if (num_video_frames == -1) {
|
||||
num_video_frames = x->ne[3];
|
||||
num_video_frames = static_cast<int>(x->ne[3]);
|
||||
}
|
||||
|
||||
x = to_backend(x);
|
||||
@@ -645,7 +651,7 @@ struct UNetModelRunner : public GGMLRunner {
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
bool compute(int n_threads,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
@@ -665,7 +671,7 @@ struct UNetModelRunner : public GGMLRunner {
|
||||
return build_graph(x, timesteps, context, c_concat, y, num_video_frames, controls, control_strength);
|
||||
};
|
||||
|
||||
GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
return GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
|
||||
void test() {
|
||||
@@ -700,12 +706,12 @@ struct UNetModelRunner : public GGMLRunner {
|
||||
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int t0 = ggml_time_ms();
|
||||
int64_t t0 = ggml_time_ms();
|
||||
compute(8, x, timesteps, context, nullptr, y, num_video_frames, {}, 0.f, &out, work_ctx);
|
||||
int t1 = ggml_time_ms();
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("unet test done in %dms", t1 - t0);
|
||||
LOG_DEBUG("unet test done in %lldms", t1 - t0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -9,12 +9,15 @@ struct UpscalerGGML {
|
||||
std::shared_ptr<ESRGAN> esrgan_upscaler;
|
||||
std::string esrgan_path;
|
||||
int n_threads;
|
||||
bool direct = false;
|
||||
bool direct = false;
|
||||
int tile_size = 128;
|
||||
|
||||
UpscalerGGML(int n_threads,
|
||||
bool direct = false)
|
||||
bool direct = false,
|
||||
int tile_size = 128)
|
||||
: n_threads(n_threads),
|
||||
direct(direct) {
|
||||
direct(direct),
|
||||
tile_size(tile_size) {
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& esrgan_path,
|
||||
@@ -51,7 +54,7 @@ struct UpscalerGGML {
|
||||
backend = ggml_backend_cpu_init();
|
||||
}
|
||||
LOG_INFO("Upscaler weight type: %s", ggml_type_name(model_data_type));
|
||||
esrgan_upscaler = std::make_shared<ESRGAN>(backend, offload_params_to_cpu, model_loader.get_tensor_storage_map());
|
||||
esrgan_upscaler = std::make_shared<ESRGAN>(backend, offload_params_to_cpu, tile_size, model_loader.get_tensor_storage_map());
|
||||
if (direct) {
|
||||
esrgan_upscaler->set_conv2d_direct_enabled(true);
|
||||
}
|
||||
@@ -113,14 +116,15 @@ struct upscaler_ctx_t {
|
||||
upscaler_ctx_t* new_upscaler_ctx(const char* esrgan_path_c_str,
|
||||
bool offload_params_to_cpu,
|
||||
bool direct,
|
||||
int n_threads) {
|
||||
int n_threads,
|
||||
int tile_size) {
|
||||
upscaler_ctx_t* upscaler_ctx = (upscaler_ctx_t*)malloc(sizeof(upscaler_ctx_t));
|
||||
if (upscaler_ctx == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
std::string esrgan_path(esrgan_path_c_str);
|
||||
|
||||
upscaler_ctx->upscaler = new UpscalerGGML(n_threads, direct);
|
||||
upscaler_ctx->upscaler = new UpscalerGGML(n_threads, direct, tile_size);
|
||||
if (upscaler_ctx->upscaler == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <cstdarg>
|
||||
#include <fstream>
|
||||
#include <locale>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -56,7 +57,7 @@ void replace_all_chars(std::string& str, char target, char replacement) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string format(const char* fmt, ...) {
|
||||
std::string sd_format(const char* fmt, ...) {
|
||||
va_list ap;
|
||||
va_list ap2;
|
||||
va_start(ap, fmt);
|
||||
@@ -94,23 +95,71 @@ bool is_directory(const std::string& path) {
|
||||
return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
|
||||
std::string get_full_path(const std::string& dir, const std::string& filename) {
|
||||
std::string full_path = dir + "\\" + filename;
|
||||
class MmapWrapperImpl : public MmapWrapper {
|
||||
public:
|
||||
MmapWrapperImpl(void* data, size_t size, HANDLE hfile, HANDLE hmapping)
|
||||
: MmapWrapper(data, size), hfile_(hfile), hmapping_(hmapping) {}
|
||||
|
||||
WIN32_FIND_DATA find_file_data;
|
||||
HANDLE hFind = FindFirstFile(full_path.c_str(), &find_file_data);
|
||||
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
FindClose(hFind);
|
||||
return full_path;
|
||||
} else {
|
||||
return "";
|
||||
~MmapWrapperImpl() override {
|
||||
UnmapViewOfFile(data_);
|
||||
CloseHandle(hmapping_);
|
||||
CloseHandle(hfile_);
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE hfile_;
|
||||
HANDLE hmapping_;
|
||||
};
|
||||
|
||||
std::unique_ptr<MmapWrapper> MmapWrapper::create(const std::string& filename) {
|
||||
void* mapped_data = nullptr;
|
||||
size_t file_size = 0;
|
||||
|
||||
HANDLE file_handle = CreateFileA(
|
||||
filename.c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LARGE_INTEGER size;
|
||||
if (!GetFileSizeEx(file_handle, &size)) {
|
||||
CloseHandle(file_handle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file_size = static_cast<size_t>(size.QuadPart);
|
||||
|
||||
HANDLE mapping_handle = CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
|
||||
if (mapping_handle == NULL) {
|
||||
CloseHandle(file_handle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mapped_data = MapViewOfFile(mapping_handle, FILE_MAP_READ, 0, 0, file_size);
|
||||
|
||||
if (mapped_data == NULL) {
|
||||
CloseHandle(mapping_handle);
|
||||
CloseHandle(file_handle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<MmapWrapperImpl>(mapped_data, file_size, file_handle, mapping_handle);
|
||||
}
|
||||
|
||||
#else // Unix
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool file_exists(const std::string& filename) {
|
||||
struct stat buffer;
|
||||
@@ -122,32 +171,68 @@ bool is_directory(const std::string& path) {
|
||||
return (stat(path.c_str(), &buffer) == 0 && S_ISDIR(buffer.st_mode));
|
||||
}
|
||||
|
||||
// TODO: add windows version
|
||||
std::string get_full_path(const std::string& dir, const std::string& filename) {
|
||||
DIR* dp = opendir(dir.c_str());
|
||||
class MmapWrapperImpl : public MmapWrapper {
|
||||
public:
|
||||
MmapWrapperImpl(void* data, size_t size)
|
||||
: MmapWrapper(data, size) {}
|
||||
|
||||
if (dp != nullptr) {
|
||||
struct dirent* entry;
|
||||
~MmapWrapperImpl() override {
|
||||
munmap(data_, size_);
|
||||
}
|
||||
};
|
||||
|
||||
while ((entry = readdir(dp)) != nullptr) {
|
||||
if (strcasecmp(entry->d_name, filename.c_str()) == 0) {
|
||||
closedir(dp);
|
||||
return dir + "/" + entry->d_name;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
std::unique_ptr<MmapWrapper> MmapWrapper::create(const std::string& filename) {
|
||||
int file_descriptor = open(filename.c_str(), O_RDONLY);
|
||||
if (file_descriptor == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return "";
|
||||
int mmap_flags = MAP_PRIVATE;
|
||||
|
||||
#ifdef __linux__
|
||||
// performance flags used by llama.cpp
|
||||
// posix_fadvise(file_descriptor, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
// mmap_flags |= MAP_POPULATE;
|
||||
#endif
|
||||
|
||||
struct stat sb;
|
||||
if (fstat(file_descriptor, &sb) == -1) {
|
||||
close(file_descriptor);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t file_size = sb.st_size;
|
||||
|
||||
void* mapped_data = mmap(NULL, file_size, PROT_READ, mmap_flags, file_descriptor, 0);
|
||||
|
||||
close(file_descriptor);
|
||||
|
||||
if (mapped_data == MAP_FAILED) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
// performance flags used by llama.cpp
|
||||
// posix_madvise(mapped_data, file_size, POSIX_MADV_WILLNEED);
|
||||
#endif
|
||||
|
||||
return std::make_unique<MmapWrapperImpl>(mapped_data, file_size);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool MmapWrapper::copy_data(void* buf, size_t n, size_t offset) const {
|
||||
if (offset >= size_ || n > (size_ - offset)) {
|
||||
return false;
|
||||
}
|
||||
std::memcpy(buf, data() + offset, n);
|
||||
return true;
|
||||
}
|
||||
|
||||
// get_num_physical_cores is copy from
|
||||
// https://github.com/ggerganov/llama.cpp/blob/master/examples/common.cpp
|
||||
// LICENSE: https://github.com/ggerganov/llama.cpp/blob/master/LICENSE
|
||||
int32_t get_num_physical_cores() {
|
||||
int32_t sd_get_num_physical_cores() {
|
||||
#ifdef __linux__
|
||||
// enumerate the set of thread siblings, num entries is num cores
|
||||
std::unordered_set<std::string> siblings;
|
||||
@@ -186,6 +271,7 @@ static sd_progress_cb_t sd_progress_cb = nullptr;
|
||||
void* sd_progress_cb_data = nullptr;
|
||||
|
||||
static sd_preview_cb_t sd_preview_cb = nullptr;
|
||||
static void* sd_preview_cb_data = nullptr;
|
||||
preview_t sd_preview_mode = PREVIEW_NONE;
|
||||
int sd_preview_interval = 1;
|
||||
bool sd_preview_denoised = true;
|
||||
@@ -272,13 +358,16 @@ void pretty_progress(int step, int steps, float time) {
|
||||
}
|
||||
}
|
||||
progress += "|";
|
||||
printf(time > 1.0f ? "\r%s %i/%i - %.2fs/it" : "\r%s %i/%i - %.2fit/s\033[K",
|
||||
progress.c_str(), step, steps,
|
||||
time > 1.0f || time == 0 ? time : (1.0f / time));
|
||||
fflush(stdout); // for linux
|
||||
if (step == steps) {
|
||||
printf("\n");
|
||||
|
||||
const char* lf = (step == steps ? "\n" : "");
|
||||
const char* unit = "s/it";
|
||||
float speed = time;
|
||||
if (speed < 1.0f && speed > 0.f) {
|
||||
speed = 1.0f / speed;
|
||||
unit = "it/s";
|
||||
}
|
||||
printf("\r%s %i/%i - %.2f%s\033[K%s", progress.c_str(), step, steps, speed, unit, lf);
|
||||
fflush(stdout); // for linux
|
||||
}
|
||||
|
||||
std::string ltrim(const std::string& s) {
|
||||
@@ -334,8 +423,9 @@ void sd_set_progress_callback(sd_progress_cb_t cb, void* data) {
|
||||
sd_progress_cb = cb;
|
||||
sd_progress_cb_data = data;
|
||||
}
|
||||
void sd_set_preview_callback(sd_preview_cb_t cb, preview_t mode = PREVIEW_PROJ, int interval = 1, bool denoised = true, bool noisy = false) {
|
||||
void sd_set_preview_callback(sd_preview_cb_t cb, preview_t mode, int interval, bool denoised, bool noisy, void* data) {
|
||||
sd_preview_cb = cb;
|
||||
sd_preview_cb_data = data;
|
||||
sd_preview_mode = mode;
|
||||
sd_preview_interval = interval;
|
||||
sd_preview_denoised = denoised;
|
||||
@@ -345,6 +435,9 @@ void sd_set_preview_callback(sd_preview_cb_t cb, preview_t mode = PREVIEW_PROJ,
|
||||
sd_preview_cb_t sd_get_preview_callback() {
|
||||
return sd_preview_cb;
|
||||
}
|
||||
void* sd_get_preview_callback_data() {
|
||||
return sd_preview_cb_data;
|
||||
}
|
||||
|
||||
preview_t sd_get_preview_mode() {
|
||||
return sd_preview_mode;
|
||||
@@ -369,19 +462,19 @@ const char* sd_get_system_info() {
|
||||
static char buffer[1024];
|
||||
std::stringstream ss;
|
||||
ss << "System Info: \n";
|
||||
ss << " SSE3 = " << ggml_cpu_has_sse3() << std::endl;
|
||||
ss << " AVX = " << ggml_cpu_has_avx() << std::endl;
|
||||
ss << " AVX2 = " << ggml_cpu_has_avx2() << std::endl;
|
||||
ss << " AVX512 = " << ggml_cpu_has_avx512() << std::endl;
|
||||
ss << " AVX512_VBMI = " << ggml_cpu_has_avx512_vbmi() << std::endl;
|
||||
ss << " AVX512_VNNI = " << ggml_cpu_has_avx512_vnni() << std::endl;
|
||||
ss << " FMA = " << ggml_cpu_has_fma() << std::endl;
|
||||
ss << " NEON = " << ggml_cpu_has_neon() << std::endl;
|
||||
ss << " ARM_FMA = " << ggml_cpu_has_arm_fma() << std::endl;
|
||||
ss << " F16C = " << ggml_cpu_has_f16c() << std::endl;
|
||||
ss << " FP16_VA = " << ggml_cpu_has_fp16_va() << std::endl;
|
||||
ss << " WASM_SIMD = " << ggml_cpu_has_wasm_simd() << std::endl;
|
||||
ss << " VSX = " << ggml_cpu_has_vsx() << std::endl;
|
||||
ss << " SSE3 = " << ggml_cpu_has_sse3() << " | ";
|
||||
ss << " AVX = " << ggml_cpu_has_avx() << " | ";
|
||||
ss << " AVX2 = " << ggml_cpu_has_avx2() << " | ";
|
||||
ss << " AVX512 = " << ggml_cpu_has_avx512() << " | ";
|
||||
ss << " AVX512_VBMI = " << ggml_cpu_has_avx512_vbmi() << " | ";
|
||||
ss << " AVX512_VNNI = " << ggml_cpu_has_avx512_vnni() << " | ";
|
||||
ss << " FMA = " << ggml_cpu_has_fma() << " | ";
|
||||
ss << " NEON = " << ggml_cpu_has_neon() << " | ";
|
||||
ss << " ARM_FMA = " << ggml_cpu_has_arm_fma() << " | ";
|
||||
ss << " F16C = " << ggml_cpu_has_f16c() << " | ";
|
||||
ss << " FP16_VA = " << ggml_cpu_has_fp16_va() << " | ";
|
||||
ss << " WASM_SIMD = " << ggml_cpu_has_wasm_simd() << " | ";
|
||||
ss << " VSX = " << ggml_cpu_has_vsx() << " | ";
|
||||
snprintf(buffer, sizeof(buffer), "%s", ss.str().c_str());
|
||||
return buffer;
|
||||
}
|
||||
@@ -395,7 +488,7 @@ sd_image_f32_t sd_image_t_to_sd_image_f32_t(sd_image_t image) {
|
||||
// Allocate memory for float data
|
||||
converted_image.data = (float*)malloc(image.width * image.height * image.channel * sizeof(float));
|
||||
|
||||
for (int i = 0; i < image.width * image.height * image.channel; i++) {
|
||||
for (uint32_t i = 0; i < image.width * image.height * image.channel; i++) {
|
||||
// Convert uint8_t to float
|
||||
converted_image.data[i] = (float)image.data[i];
|
||||
}
|
||||
@@ -427,7 +520,7 @@ sd_image_f32_t resize_sd_image_f32_t(sd_image_f32_t image, int target_width, int
|
||||
uint32_t x2 = std::min(x1 + 1, image.width - 1);
|
||||
uint32_t y2 = std::min(y1 + 1, image.height - 1);
|
||||
|
||||
for (int k = 0; k < image.channel; k++) {
|
||||
for (uint32_t k = 0; k < image.channel; k++) {
|
||||
float v1 = *(image.data + y1 * image.width * image.channel + x1 * image.channel + k);
|
||||
float v2 = *(image.data + y1 * image.width * image.channel + x2 * image.channel + k);
|
||||
float v3 = *(image.data + y2 * image.width * image.channel + x1 * image.channel + k);
|
||||
@@ -447,9 +540,9 @@ sd_image_f32_t resize_sd_image_f32_t(sd_image_f32_t image, int target_width, int
|
||||
}
|
||||
|
||||
void normalize_sd_image_f32_t(sd_image_f32_t image, float means[3], float stds[3]) {
|
||||
for (int y = 0; y < image.height; y++) {
|
||||
for (int x = 0; x < image.width; x++) {
|
||||
for (int k = 0; k < image.channel; k++) {
|
||||
for (uint32_t y = 0; y < image.height; y++) {
|
||||
for (uint32_t x = 0; x < image.width; x++) {
|
||||
for (uint32_t k = 0; k < image.channel; k++) {
|
||||
int index = (y * image.width + x) * image.channel + k;
|
||||
image.data[index] = (image.data[index] - means[k]) / stds[k];
|
||||
}
|
||||
@@ -458,8 +551,8 @@ void normalize_sd_image_f32_t(sd_image_f32_t image, float means[3], float stds[3
|
||||
}
|
||||
|
||||
// Constants for means and std
|
||||
float means[3] = {0.48145466, 0.4578275, 0.40821073};
|
||||
float stds[3] = {0.26862954, 0.26130258, 0.27577711};
|
||||
float means[3] = {0.48145466f, 0.4578275f, 0.40821073f};
|
||||
float stds[3] = {0.26862954f, 0.26130258f, 0.27577711f};
|
||||
|
||||
// Function to clip and preprocess sd_image_f32_t
|
||||
sd_image_f32_t clip_preprocess(sd_image_f32_t image, int target_width, int target_height) {
|
||||
@@ -483,7 +576,7 @@ sd_image_f32_t clip_preprocess(sd_image_f32_t image, int target_width, int targe
|
||||
uint32_t x2 = std::min(x1 + 1, image.width - 1);
|
||||
uint32_t y2 = std::min(y1 + 1, image.height - 1);
|
||||
|
||||
for (int k = 0; k < image.channel; k++) {
|
||||
for (uint32_t k = 0; k < image.channel; k++) {
|
||||
float v1 = *(image.data + y1 * image.width * image.channel + x1 * image.channel + k);
|
||||
float v2 = *(image.data + y1 * image.width * image.channel + x2 * image.channel + k);
|
||||
float v3 = *(image.data + y2 * image.width * image.channel + x1 * image.channel + k);
|
||||
@@ -509,11 +602,11 @@ sd_image_f32_t clip_preprocess(sd_image_f32_t image, int target_width, int targe
|
||||
result.channel = image.channel;
|
||||
result.data = (float*)malloc(target_height * target_width * image.channel * sizeof(float));
|
||||
|
||||
for (int k = 0; k < image.channel; k++) {
|
||||
for (int i = 0; i < result.height; i++) {
|
||||
for (int j = 0; j < result.width; j++) {
|
||||
int src_y = std::min(i + h_offset, resized_height - 1);
|
||||
int src_x = std::min(j + w_offset, resized_width - 1);
|
||||
for (uint32_t k = 0; k < image.channel; k++) {
|
||||
for (uint32_t i = 0; i < result.height; i++) {
|
||||
for (uint32_t j = 0; j < result.width; j++) {
|
||||
int src_y = std::min(static_cast<int>(i + h_offset), resized_height - 1);
|
||||
int src_x = std::min(static_cast<int>(j + w_offset), resized_width - 1);
|
||||
*(result.data + i * result.width * image.channel + j * image.channel + k) =
|
||||
fmin(fmax(*(resized_data + src_y * resized_width * image.channel + src_x * image.channel + k), 0.0f), 255.0f) / 255.0f;
|
||||
}
|
||||
@@ -524,9 +617,9 @@ sd_image_f32_t clip_preprocess(sd_image_f32_t image, int target_width, int targe
|
||||
free(resized_data);
|
||||
|
||||
// Normalize
|
||||
for (int k = 0; k < image.channel; k++) {
|
||||
for (int i = 0; i < result.height; i++) {
|
||||
for (int j = 0; j < result.width; j++) {
|
||||
for (uint32_t k = 0; k < image.channel; k++) {
|
||||
for (uint32_t i = 0; i < result.height; i++) {
|
||||
for (uint32_t j = 0; j < result.width; j++) {
|
||||
// *(result.data + i * size * image.channel + j * image.channel + k) = 0.5f;
|
||||
int offset = i * result.width * image.channel + j * image.channel + k;
|
||||
float value = *(result.data + offset);
|
||||
@@ -547,6 +640,8 @@ sd_image_f32_t clip_preprocess(sd_image_f32_t image, int target_width, int targe
|
||||
// (abc) - increases attention to abc by a multiplier of 1.1
|
||||
// (abc:3.12) - increases attention to abc by a multiplier of 3.12
|
||||
// [abc] - decreases attention to abc by a multiplier of 1.1
|
||||
// BREAK - separates the prompt into conceptually distinct parts for sequential processing
|
||||
// B - internal helper pattern; prevents 'B' in 'BREAK' from being consumed as normal text
|
||||
// \( - literal character '('
|
||||
// \[ - literal character '['
|
||||
// \) - literal character ')'
|
||||
@@ -582,7 +677,7 @@ std::vector<std::pair<std::string, float>> parse_prompt_attention(const std::str
|
||||
float round_bracket_multiplier = 1.1f;
|
||||
float square_bracket_multiplier = 1 / 1.1f;
|
||||
|
||||
std::regex re_attention(R"(\\\(|\\\)|\\\[|\\\]|\\\\|\\|\(|\[|:([+-]?[.\d]+)\)|\)|\]|[^\\()\[\]:]+|:)");
|
||||
std::regex re_attention(R"(\\\(|\\\)|\\\[|\\\]|\\\\|\\|\(|\[|:([+-]?[.\d]+)\)|\)|\]|\bBREAK\b|[^\\()\[\]:B]+|:|\bB)");
|
||||
std::regex re_break(R"(\s*\bBREAK\b\s*)");
|
||||
|
||||
auto multiply_range = [&](int start_position, float multiplier) {
|
||||
@@ -591,7 +686,7 @@ std::vector<std::pair<std::string, float>> parse_prompt_attention(const std::str
|
||||
}
|
||||
};
|
||||
|
||||
std::smatch m;
|
||||
std::smatch m, m2;
|
||||
std::string remaining_text = text;
|
||||
|
||||
while (std::regex_search(remaining_text, m, re_attention)) {
|
||||
@@ -615,6 +710,8 @@ std::vector<std::pair<std::string, float>> parse_prompt_attention(const std::str
|
||||
square_brackets.pop_back();
|
||||
} else if (text == "\\(") {
|
||||
res.push_back({text.substr(1), 1.0f});
|
||||
} else if (std::regex_search(text, m2, re_break)) {
|
||||
res.push_back({"BREAK", -1.0f});
|
||||
} else {
|
||||
res.push_back({text, 1.0f});
|
||||
}
|
||||
@@ -645,4 +742,4 @@ std::vector<std::pair<std::string, float>> parse_prompt_attention(const std::str
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __UTIL_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -14,7 +15,7 @@ bool ends_with(const std::string& str, const std::string& ending);
|
||||
bool starts_with(const std::string& str, const std::string& start);
|
||||
bool contains(const std::string& str, const std::string& substr);
|
||||
|
||||
std::string format(const char* fmt, ...);
|
||||
std::string sd_format(const char* fmt, ...);
|
||||
|
||||
void replace_all_chars(std::string& str, char target, char replacement);
|
||||
|
||||
@@ -22,7 +23,6 @@ int round_up_to(int value, int base);
|
||||
|
||||
bool file_exists(const std::string& filename);
|
||||
bool is_directory(const std::string& path);
|
||||
std::string get_full_path(const std::string& dir, const std::string& filename);
|
||||
|
||||
std::u32string utf8_to_utf32(const std::string& utf8_str);
|
||||
std::string utf32_to_utf8(const std::u32string& utf32_str);
|
||||
@@ -44,6 +44,28 @@ sd_image_f32_t resize_sd_image_f32_t(sd_image_f32_t image, int target_width, int
|
||||
|
||||
sd_image_f32_t clip_preprocess(sd_image_f32_t image, int target_width, int target_height);
|
||||
|
||||
class MmapWrapper {
|
||||
public:
|
||||
static std::unique_ptr<MmapWrapper> create(const std::string& filename);
|
||||
|
||||
virtual ~MmapWrapper() = default;
|
||||
|
||||
MmapWrapper(const MmapWrapper&) = delete;
|
||||
MmapWrapper& operator=(const MmapWrapper&) = delete;
|
||||
MmapWrapper(MmapWrapper&&) = delete;
|
||||
MmapWrapper& operator=(MmapWrapper&&) = delete;
|
||||
|
||||
const uint8_t* data() const { return static_cast<uint8_t*>(data_); }
|
||||
size_t size() const { return size_; }
|
||||
bool copy_data(void* buf, size_t n, size_t offset) const;
|
||||
|
||||
protected:
|
||||
MmapWrapper(void* data, size_t size)
|
||||
: data_(data), size_(size) {}
|
||||
void* data_ = nullptr;
|
||||
size_t size_ = 0;
|
||||
};
|
||||
|
||||
std::string path_join(const std::string& p1, const std::string& p2);
|
||||
std::vector<std::string> split_string(const std::string& str, char delimiter);
|
||||
void pretty_progress(int step, int steps, float time);
|
||||
@@ -58,6 +80,7 @@ sd_progress_cb_t sd_get_progress_callback();
|
||||
void* sd_get_progress_callback_data();
|
||||
|
||||
sd_preview_cb_t sd_get_preview_callback();
|
||||
void* sd_get_preview_callback_data();
|
||||
preview_t sd_get_preview_mode();
|
||||
int sd_get_preview_interval();
|
||||
bool sd_should_preview_denoised();
|
||||
|
||||
@@ -127,8 +127,6 @@ public:
|
||||
q = q_proj->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
k = k_proj->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
v = v_proj->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
|
||||
v = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, v, 1, 0, 2, 3)); // [N, in_channels, h * w]
|
||||
} else {
|
||||
q = q_proj->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
q = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, q, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
@@ -138,11 +136,12 @@ public:
|
||||
k = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, k, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
k = ggml_reshape_3d(ctx->ggml_ctx, k, c, h * w, n); // [N, h * w, in_channels]
|
||||
|
||||
v = v_proj->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
v = ggml_reshape_3d(ctx->ggml_ctx, v, h * w, c, n); // [N, in_channels, h * w]
|
||||
v = v_proj->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
v = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, v, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
v = ggml_reshape_3d(ctx->ggml_ctx, v, c, h * w, n); // [N, h * w, in_channels]
|
||||
}
|
||||
|
||||
h_ = ggml_ext_attention(ctx->ggml_ctx, q, k, v, false); // [N, h * w, in_channels]
|
||||
h_ = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, 1, nullptr, true, ctx->flash_attn_enabled);
|
||||
|
||||
if (use_linear) {
|
||||
h_ = proj_out->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
@@ -166,18 +165,18 @@ public:
|
||||
AE3DConv(int64_t in_channels,
|
||||
int64_t out_channels,
|
||||
std::pair<int, int> kernel_size,
|
||||
int64_t video_kernel_size = 3,
|
||||
int video_kernel_size = 3,
|
||||
std::pair<int, int> stride = {1, 1},
|
||||
std::pair<int, int> padding = {0, 0},
|
||||
std::pair<int, int> dilation = {1, 1},
|
||||
bool bias = true)
|
||||
: Conv2d(in_channels, out_channels, kernel_size, stride, padding, dilation, bias) {
|
||||
int64_t kernel_padding = video_kernel_size / 2;
|
||||
blocks["time_mix_conv"] = std::shared_ptr<GGMLBlock>(new Conv3dnx1x1(out_channels,
|
||||
out_channels,
|
||||
video_kernel_size,
|
||||
1,
|
||||
kernel_padding));
|
||||
int kernel_padding = video_kernel_size / 2;
|
||||
blocks["time_mix_conv"] = std::shared_ptr<GGMLBlock>(new Conv3d(out_channels,
|
||||
out_channels,
|
||||
{video_kernel_size, 1, 1},
|
||||
{1, 1, 1},
|
||||
{kernel_padding, 0, 0}));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
@@ -186,7 +185,7 @@ public:
|
||||
// skip_video always False
|
||||
// x: [N, IC, IH, IW]
|
||||
// result: [N, OC, OH, OW]
|
||||
auto time_mix_conv = std::dynamic_pointer_cast<Conv3dnx1x1>(blocks["time_mix_conv"]);
|
||||
auto time_mix_conv = std::dynamic_pointer_cast<Conv3d>(blocks["time_mix_conv"]);
|
||||
|
||||
x = Conv2d::forward(ctx, x);
|
||||
// timesteps = x.shape[0]
|
||||
@@ -254,8 +253,8 @@ public:
|
||||
|
||||
float alpha = get_alpha();
|
||||
x = ggml_add(ctx->ggml_ctx,
|
||||
ggml_scale(ctx->ggml_ctx, x, alpha),
|
||||
ggml_scale(ctx->ggml_ctx, x_mix, 1.0f - alpha));
|
||||
ggml_ext_scale(ctx->ggml_ctx, x, alpha),
|
||||
ggml_ext_scale(ctx->ggml_ctx, x_mix, 1.0f - alpha));
|
||||
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // b c t (h w) -> b t c (h w)
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W, H, C, T * B); // b t c (h w) -> (b t) c h w
|
||||
@@ -409,8 +408,8 @@ public:
|
||||
z_channels(z_channels),
|
||||
video_decoder(video_decoder),
|
||||
video_kernel_size(video_kernel_size) {
|
||||
size_t num_resolutions = ch_mult.size();
|
||||
int block_in = ch * ch_mult[num_resolutions - 1];
|
||||
int num_resolutions = static_cast<int>(ch_mult.size());
|
||||
int block_in = ch * ch_mult[num_resolutions - 1];
|
||||
|
||||
blocks["conv_in"] = std::shared_ptr<GGMLBlock>(new Conv2d(z_channels, block_in, {3, 3}, {1, 1}, {1, 1}));
|
||||
|
||||
@@ -461,7 +460,7 @@ public:
|
||||
h = mid_block_2->forward(ctx, h); // [N, block_in, h, w]
|
||||
|
||||
// upsampling
|
||||
size_t num_resolutions = ch_mult.size();
|
||||
int num_resolutions = static_cast<int>(ch_mult.size());
|
||||
for (int i = num_resolutions - 1; i >= 0; i--) {
|
||||
for (int j = 0; j < num_res_blocks + 1; j++) {
|
||||
std::string name = "up." + std::to_string(i) + ".block." + std::to_string(j);
|
||||
@@ -487,6 +486,7 @@ public:
|
||||
// ldm.models.autoencoder.AutoencoderKL
|
||||
class AutoencodingEngine : public GGMLBlock {
|
||||
protected:
|
||||
SDVersion version;
|
||||
bool decode_only = true;
|
||||
bool use_video_decoder = false;
|
||||
bool use_quant = true;
|
||||
@@ -507,10 +507,15 @@ public:
|
||||
bool decode_only = true,
|
||||
bool use_linear_projection = false,
|
||||
bool use_video_decoder = false)
|
||||
: decode_only(decode_only), use_video_decoder(use_video_decoder) {
|
||||
: version(version), decode_only(decode_only), use_video_decoder(use_video_decoder) {
|
||||
if (sd_version_is_dit(version)) {
|
||||
dd_config.z_channels = 16;
|
||||
use_quant = false;
|
||||
if (sd_version_is_flux2(version)) {
|
||||
dd_config.z_channels = 32;
|
||||
embed_dim = 32;
|
||||
} else {
|
||||
use_quant = false;
|
||||
dd_config.z_channels = 16;
|
||||
}
|
||||
}
|
||||
if (use_video_decoder) {
|
||||
use_quant = false;
|
||||
@@ -547,6 +552,24 @@ public:
|
||||
|
||||
struct ggml_tensor* decode(GGMLRunnerContext* ctx, struct ggml_tensor* z) {
|
||||
// z: [N, z_channels, h, w]
|
||||
if (sd_version_is_flux2(version)) {
|
||||
// [N, C*p*p, h, w] -> [N, C, h*p, w*p]
|
||||
int64_t p = 2;
|
||||
|
||||
int64_t N = z->ne[3];
|
||||
int64_t C = z->ne[2] / p / p;
|
||||
int64_t h = z->ne[1];
|
||||
int64_t w = z->ne[0];
|
||||
int64_t H = h * p;
|
||||
int64_t W = w * p;
|
||||
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, w * h, p * p, C, N); // [N, C, p*p, h*w]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, z, 1, 0, 2, 3)); // [N, C, h*w, p*p]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, p, p, w, h * C * N); // [N*C*h, w, p, p]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, z, 0, 2, 1, 3)); // [N*C*h, p, w, p]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, W, H, C, N); // [N, C, h*p, w*p]
|
||||
}
|
||||
|
||||
if (use_quant) {
|
||||
auto post_quant_conv = std::dynamic_pointer_cast<Conv2d>(blocks["post_quant_conv"]);
|
||||
z = post_quant_conv->forward(ctx, z); // [N, z_channels, h, w]
|
||||
@@ -563,19 +586,37 @@ public:
|
||||
// x: [N, in_channels, h, w]
|
||||
auto encoder = std::dynamic_pointer_cast<Encoder>(blocks["encoder"]);
|
||||
|
||||
auto h = encoder->forward(ctx, x); // [N, 2*z_channels, h/8, w/8]
|
||||
auto z = encoder->forward(ctx, x); // [N, 2*z_channels, h/8, w/8]
|
||||
if (use_quant) {
|
||||
auto quant_conv = std::dynamic_pointer_cast<Conv2d>(blocks["quant_conv"]);
|
||||
h = quant_conv->forward(ctx, h); // [N, 2*embed_dim, h/8, w/8]
|
||||
z = quant_conv->forward(ctx, z); // [N, 2*embed_dim, h/8, w/8]
|
||||
}
|
||||
return h;
|
||||
if (sd_version_is_flux2(version)) {
|
||||
z = ggml_ext_chunk(ctx->ggml_ctx, z, 2, 2)[0];
|
||||
|
||||
// [N, C, H, W] -> [N, C*p*p, H/p, W/p]
|
||||
int64_t p = 2;
|
||||
int64_t N = z->ne[3];
|
||||
int64_t C = z->ne[2];
|
||||
int64_t H = z->ne[1];
|
||||
int64_t W = z->ne[0];
|
||||
int64_t h = H / p;
|
||||
int64_t w = W / p;
|
||||
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, p, w, p, h * C * N); // [N*C*h, p, w, p]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, z, 0, 2, 1, 3)); // [N*C*h, w, p, p]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, p * p, w * h, C, N); // [N, C, h*w, p*p]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, z, 1, 0, 2, 3)); // [N, C, p*p, h*w]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, w, h, p * p * C, N); // [N, C*p*p, h*w]
|
||||
}
|
||||
return z;
|
||||
}
|
||||
};
|
||||
|
||||
struct VAE : public GGMLRunner {
|
||||
VAE(ggml_backend_t backend, bool offload_params_to_cpu)
|
||||
: GGMLRunner(backend, offload_params_to_cpu) {}
|
||||
virtual void compute(const int n_threads,
|
||||
virtual bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
@@ -587,7 +628,7 @@ struct VAE : public GGMLRunner {
|
||||
struct FakeVAE : public VAE {
|
||||
FakeVAE(ggml_backend_t backend, bool offload_params_to_cpu)
|
||||
: VAE(backend, offload_params_to_cpu) {}
|
||||
void compute(const int n_threads,
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
@@ -599,6 +640,7 @@ struct FakeVAE : public VAE {
|
||||
float value = ggml_ext_tensor_get_f32(z, i0, i1, i2, i3);
|
||||
ggml_ext_tensor_set_f32(*output, value, i0, i1, i2, i3);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) override {}
|
||||
@@ -669,7 +711,7 @@ struct AutoEncoderKL : public VAE {
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(const int n_threads,
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
@@ -680,7 +722,7 @@ struct AutoEncoderKL : public VAE {
|
||||
};
|
||||
// ggml_set_f32(z, 0.5f);
|
||||
// print_ggml_tensor(z);
|
||||
GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
return GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
|
||||
void test() {
|
||||
@@ -702,12 +744,12 @@ struct AutoEncoderKL : public VAE {
|
||||
print_ggml_tensor(x);
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int t0 = ggml_time_ms();
|
||||
int64_t t0 = ggml_time_ms();
|
||||
compute(8, x, false, &out, work_ctx);
|
||||
int t1 = ggml_time_ms();
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("encode test done in %dms", t1 - t0);
|
||||
LOG_DEBUG("encode test done in %lldms", t1 - t0);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
@@ -720,12 +762,12 @@ struct AutoEncoderKL : public VAE {
|
||||
print_ggml_tensor(z);
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int t0 = ggml_time_ms();
|
||||
int64_t t0 = ggml_time_ms();
|
||||
compute(8, z, true, &out, work_ctx);
|
||||
int t1 = ggml_time_ms();
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("decode test done in %dms", t1 - t0);
|
||||
LOG_DEBUG("decode test done in %lldms", t1 - t0);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||