mirror of
https://github.com/ggerganov/whisper.cpp.git
synced 2025-07-06 17:31:44 +02:00
Compare commits
18 Commits
talk.llama
...
llama-podc
Author | SHA1 | Date | |
---|---|---|---|
c456ca476b | |||
0a2d1210bc | |||
859ffc994e | |||
5e6e2187a3 | |||
a7f1f33715 | |||
86ecfc6333 | |||
18e6fb0287 | |||
0f759f125d | |||
eefed45e37 | |||
aac1710afb | |||
21c1e6afc5 | |||
a47e812a54 | |||
42c6855103 | |||
0be9cd3497 | |||
e5c197d8aa | |||
7cd1d3bc34 | |||
82637b8e9f | |||
4a0deb8b1e |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,7 +1,5 @@
|
|||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
*.mlmodel
|
|
||||||
*.mlmodelc
|
|
||||||
.cache/
|
.cache/
|
||||||
.vs/
|
.vs/
|
||||||
.vscode/
|
.vscode/
|
||||||
@ -20,6 +18,7 @@ build-sanitize-thread/
|
|||||||
/stream
|
/stream
|
||||||
/command
|
/command
|
||||||
/talk
|
/talk
|
||||||
|
/talk-llama
|
||||||
/bench
|
/bench
|
||||||
|
|
||||||
arm_neon.h
|
arm_neon.h
|
||||||
|
@ -54,8 +54,6 @@ if (APPLE)
|
|||||||
option(WHISPER_NO_AVX "whisper: disable AVX" OFF)
|
option(WHISPER_NO_AVX "whisper: disable AVX" OFF)
|
||||||
option(WHISPER_NO_AVX2 "whisper: disable AVX2" OFF)
|
option(WHISPER_NO_AVX2 "whisper: disable AVX2" OFF)
|
||||||
option(WHISPER_NO_FMA "whisper: disable FMA" OFF)
|
option(WHISPER_NO_FMA "whisper: disable FMA" OFF)
|
||||||
|
|
||||||
option(WHISPER_COREML "whisper: enable Core ML framework" OFF)
|
|
||||||
else()
|
else()
|
||||||
option(WHISPER_SUPPORT_OPENBLAS "whisper: support for OpenBLAS" OFF)
|
option(WHISPER_SUPPORT_OPENBLAS "whisper: support for OpenBLAS" OFF)
|
||||||
endif()
|
endif()
|
||||||
@ -88,12 +86,9 @@ endif()
|
|||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
# on APPLE
|
# on APPLE - include Accelerate framework
|
||||||
if (APPLE)
|
if (APPLE AND NOT WHISPER_NO_ACCELERATE)
|
||||||
# include Accelerate framework
|
|
||||||
if (NOT WHISPER_NO_ACCELERATE)
|
|
||||||
find_library(ACCELERATE_FRAMEWORK Accelerate)
|
find_library(ACCELERATE_FRAMEWORK Accelerate)
|
||||||
|
|
||||||
if (ACCELERATE_FRAMEWORK)
|
if (ACCELERATE_FRAMEWORK)
|
||||||
message(STATUS "Accelerate framework found")
|
message(STATUS "Accelerate framework found")
|
||||||
|
|
||||||
@ -102,20 +97,6 @@ if (APPLE)
|
|||||||
else()
|
else()
|
||||||
message(WARNING "Accelerate framework not found")
|
message(WARNING "Accelerate framework not found")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
|
||||||
|
|
||||||
if (WHISPER_COREML)
|
|
||||||
find_library(FOUNDATION_FRAMEWORK Foundation)
|
|
||||||
find_library(COREML_FRAMEWORK CoreML)
|
|
||||||
|
|
||||||
if (COREML_FRAMEWORK)
|
|
||||||
message(STATUS "CoreML framework found")
|
|
||||||
|
|
||||||
set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DWHISPER_USE_COREML)
|
|
||||||
else()
|
|
||||||
message(WARNING "CoreML framework not found")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WHISPER_SUPPORT_OPENBLAS)
|
if (WHISPER_SUPPORT_OPENBLAS)
|
||||||
@ -202,33 +183,6 @@ if (WHISPER_PERF)
|
|||||||
set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_PERF)
|
set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_PERF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#
|
|
||||||
# whisper.coreml - Core ML support
|
|
||||||
#
|
|
||||||
|
|
||||||
if (WHISPER_COREML)
|
|
||||||
set(TARGET whisper.coreml)
|
|
||||||
|
|
||||||
add_library(${TARGET}
|
|
||||||
coreml/whisper-encoder.h
|
|
||||||
coreml/whisper-encoder.mm
|
|
||||||
coreml/whisper-encoder-impl.h
|
|
||||||
coreml/whisper-encoder-impl.m
|
|
||||||
)
|
|
||||||
|
|
||||||
include(DefaultTargetOptions)
|
|
||||||
|
|
||||||
target_include_directories(${TARGET} PUBLIC
|
|
||||||
.
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(${TARGET} PRIVATE ${FOUNDATION_FRAMEWORK} ${COREML_FRAMEWORK})
|
|
||||||
|
|
||||||
set_target_properties(${TARGET} PROPERTIES
|
|
||||||
COMPILE_FLAGS "-fobjc-arc"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# whisper - this is the main library of the project
|
# whisper - this is the main library of the project
|
||||||
#
|
#
|
||||||
@ -248,10 +202,6 @@ target_include_directories(${TARGET} PUBLIC
|
|||||||
.
|
.
|
||||||
)
|
)
|
||||||
|
|
||||||
if (WHISPER_COREML)
|
|
||||||
target_link_libraries(${TARGET} PRIVATE whisper.coreml)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_link_libraries(${TARGET} PRIVATE ${WHISPER_EXTRA_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
target_link_libraries(${TARGET} PRIVATE ${WHISPER_EXTRA_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
|
||||||
|
62
Makefile
62
Makefile
@ -138,10 +138,6 @@ ifndef WHISPER_NO_ACCELERATE
|
|||||||
LDFLAGS += -framework Accelerate
|
LDFLAGS += -framework Accelerate
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
ifdef WHISPER_COREML
|
|
||||||
CXXFLAGS += -DWHISPER_USE_COREML
|
|
||||||
LDFLAGS += -framework Foundation -framework CoreML
|
|
||||||
endif
|
|
||||||
ifdef WHISPER_OPENBLAS
|
ifdef WHISPER_OPENBLAS
|
||||||
CFLAGS += -DGGML_USE_OPENBLAS -I/usr/local/include/openblas
|
CFLAGS += -DGGML_USE_OPENBLAS -I/usr/local/include/openblas
|
||||||
LDFLAGS += -lopenblas
|
LDFLAGS += -lopenblas
|
||||||
@ -155,12 +151,15 @@ ifneq ($(filter aarch64%,$(UNAME_M)),)
|
|||||||
CXXFLAGS += -mcpu=native
|
CXXFLAGS += -mcpu=native
|
||||||
endif
|
endif
|
||||||
ifneq ($(filter armv6%,$(UNAME_M)),)
|
ifneq ($(filter armv6%,$(UNAME_M)),)
|
||||||
# Raspberry Pi 1, 2, 3
|
# 32-bit Raspberry Pi 1, 2, 3
|
||||||
CFLAGS += -mfpu=neon-fp-armv8 -mfp16-format=ieee -mno-unaligned-access
|
CFLAGS += -mfpu=neon -mfp16-format=ieee -mno-unaligned-access
|
||||||
endif
|
endif
|
||||||
ifneq ($(filter armv7%,$(UNAME_M)),)
|
ifneq ($(filter armv7%,$(UNAME_M)),)
|
||||||
# Raspberry Pi 4
|
# 32-bit ARM, for example on Armbian or possibly raspbian
|
||||||
CFLAGS += -mfpu=neon-fp-armv8 -mfp16-format=ieee -mno-unaligned-access -funsafe-math-optimizations
|
CFLAGS += -mfpu=neon -mfp16-format=ieee -mno-unaligned-access -funsafe-math-optimizations
|
||||||
|
|
||||||
|
# 64-bit ARM, use these (TODO: auto-detect 64-bit)
|
||||||
|
# CFLAGS += -mfpu=neon-fp-armv8 -mfp16-format=ieee -mno-unaligned-access -funsafe-math-optimizations
|
||||||
endif
|
endif
|
||||||
ifneq ($(filter armv8%,$(UNAME_M)),)
|
ifneq ($(filter armv8%,$(UNAME_M)),)
|
||||||
# Raspberry Pi 4
|
# Raspberry Pi 4
|
||||||
@ -182,7 +181,7 @@ $(info I CC: $(CCV))
|
|||||||
$(info I CXX: $(CXXV))
|
$(info I CXX: $(CXXV))
|
||||||
$(info )
|
$(info )
|
||||||
|
|
||||||
default: main
|
default: main bench
|
||||||
|
|
||||||
#
|
#
|
||||||
# Build library
|
# Build library
|
||||||
@ -194,26 +193,14 @@ ggml.o: ggml.c ggml.h
|
|||||||
whisper.o: whisper.cpp whisper.h
|
whisper.o: whisper.cpp whisper.h
|
||||||
$(CXX) $(CXXFLAGS) -c whisper.cpp -o whisper.o
|
$(CXX) $(CXXFLAGS) -c whisper.cpp -o whisper.o
|
||||||
|
|
||||||
ifndef WHISPER_COREML
|
libwhisper.a: ggml.o whisper.o
|
||||||
WHISPER_OBJ = whisper.o
|
$(AR) rcs libwhisper.a ggml.o whisper.o
|
||||||
else
|
|
||||||
whisper-encoder.o: coreml/whisper-encoder.mm coreml/whisper-encoder.h
|
|
||||||
$(CXX) -O3 -I . -c coreml/whisper-encoder.mm -o whisper-encoder.o
|
|
||||||
|
|
||||||
whisper-encoder-impl.o: coreml/whisper-encoder-impl.m coreml/whisper-encoder-impl.h
|
libwhisper.so: ggml.o whisper.o
|
||||||
$(CXX) -O3 -I . -fobjc-arc -c coreml/whisper-encoder-impl.m -o whisper-encoder-impl.o
|
$(CXX) $(CXXFLAGS) -shared -o libwhisper.so ggml.o whisper.o $(LDFLAGS)
|
||||||
|
|
||||||
WHISPER_OBJ = whisper.o whisper-encoder.o whisper-encoder-impl.o
|
|
||||||
endif
|
|
||||||
|
|
||||||
libwhisper.a: ggml.o $(WHISPER_OBJ)
|
|
||||||
$(AR) rcs libwhisper.a ggml.o $(WHISPER_OBJ)
|
|
||||||
|
|
||||||
libwhisper.so: ggml.o $(WHISPER_OBJ)
|
|
||||||
$(CXX) $(CXXFLAGS) -shared -o libwhisper.so ggml.o $(WHISPER_OBJ) $(LDFLAGS)
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o main stream command talk bench libwhisper.a libwhisper.so
|
rm -f *.o main stream command talk talk-llama bench libwhisper.a libwhisper.so
|
||||||
|
|
||||||
#
|
#
|
||||||
# Examples
|
# Examples
|
||||||
@ -224,21 +211,24 @@ CC_SDL=`sdl2-config --cflags --libs`
|
|||||||
SRC_COMMON = examples/common.cpp
|
SRC_COMMON = examples/common.cpp
|
||||||
SRC_COMMON_SDL = examples/common-sdl.cpp
|
SRC_COMMON_SDL = examples/common-sdl.cpp
|
||||||
|
|
||||||
main: examples/main/main.cpp $(SRC_COMMON) ggml.o $(WHISPER_OBJ)
|
main: examples/main/main.cpp $(SRC_COMMON) ggml.o whisper.o
|
||||||
$(CXX) $(CXXFLAGS) examples/main/main.cpp $(SRC_COMMON) ggml.o $(WHISPER_OBJ) -o main $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/main/main.cpp $(SRC_COMMON) ggml.o whisper.o -o main $(LDFLAGS)
|
||||||
./main -h
|
./main -h
|
||||||
|
|
||||||
stream: examples/stream/stream.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ)
|
bench: examples/bench/bench.cpp ggml.o whisper.o
|
||||||
$(CXX) $(CXXFLAGS) examples/stream/stream.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ) -o stream $(CC_SDL) $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/bench/bench.cpp ggml.o whisper.o -o bench $(LDFLAGS)
|
||||||
|
|
||||||
command: examples/command/command.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ)
|
stream: examples/stream/stream.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o whisper.o
|
||||||
$(CXX) $(CXXFLAGS) examples/command/command.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ) -o command $(CC_SDL) $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/stream/stream.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o whisper.o -o stream $(CC_SDL) $(LDFLAGS)
|
||||||
|
|
||||||
talk: examples/talk/talk.cpp examples/talk/gpt-2.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ)
|
command: examples/command/command.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o whisper.o
|
||||||
$(CXX) $(CXXFLAGS) examples/talk/talk.cpp examples/talk/gpt-2.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ) -o talk $(CC_SDL) $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/command/command.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o whisper.o -o command $(CC_SDL) $(LDFLAGS)
|
||||||
|
|
||||||
bench: examples/bench/bench.cpp ggml.o $(WHISPER_OBJ)
|
talk: examples/talk/talk.cpp examples/talk/gpt-2.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o whisper.o
|
||||||
$(CXX) $(CXXFLAGS) examples/bench/bench.cpp ggml.o $(WHISPER_OBJ) -o bench $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/talk/talk.cpp examples/talk/gpt-2.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o whisper.o -o talk $(CC_SDL) $(LDFLAGS)
|
||||||
|
|
||||||
|
talk-llama: examples/talk-llama/talk-llama.cpp examples/talk-llama/llama.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o whisper.o
|
||||||
|
$(CXX) $(CXXFLAGS) examples/talk-llama/talk-llama.cpp examples/talk-llama/llama.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o whisper.o -o talk-llama $(CC_SDL) $(LDFLAGS)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Audio samples
|
# Audio samples
|
||||||
|
@ -313,7 +313,7 @@ whisper_print_timings: total time = 32733.52 ms
|
|||||||
## Real-time audio input example
|
## Real-time audio input example
|
||||||
|
|
||||||
This is a naive example of performing real-time inference on audio from your microphone.
|
This is a naive example of performing real-time inference on audio from your microphone.
|
||||||
The [stream](examples/stream) tool samples the audio every half a second and runs the transcription continously.
|
The [stream](examples/stream) tool samples the audio every half a second and runs the transcription continuously.
|
||||||
More info is available in [issue #10](https://github.com/ggerganov/whisper.cpp/issues/10).
|
More info is available in [issue #10](https://github.com/ggerganov/whisper.cpp/issues/10).
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ -500,6 +500,7 @@ Some of the examples are even ported to run in the browser using WebAssembly. Ch
|
|||||||
| [stream](examples/stream) | [stream.wasm](examples/stream.wasm) | Real-time transcription of raw microphone capture |
|
| [stream](examples/stream) | [stream.wasm](examples/stream.wasm) | Real-time transcription of raw microphone capture |
|
||||||
| [command](examples/command) | [command.wasm](examples/command.wasm) | Basic voice assistant example for receiving voice commands from the mic |
|
| [command](examples/command) | [command.wasm](examples/command.wasm) | Basic voice assistant example for receiving voice commands from the mic |
|
||||||
| [talk](examples/talk) | [talk.wasm](examples/talk.wasm) | Talk with a GPT-2 bot |
|
| [talk](examples/talk) | [talk.wasm](examples/talk.wasm) | Talk with a GPT-2 bot |
|
||||||
|
| [talk-llama](examples/talk-llama) | | Talk with a LLaMA bot |
|
||||||
| [whisper.objc](examples/whisper.objc) | | iOS mobile application using whisper.cpp |
|
| [whisper.objc](examples/whisper.objc) | | iOS mobile application using whisper.cpp |
|
||||||
| [whisper.swiftui](examples/whisper.swiftui) | | SwiftUI iOS / macOS application using whisper.cpp |
|
| [whisper.swiftui](examples/whisper.swiftui) | | SwiftUI iOS / macOS application using whisper.cpp |
|
||||||
| [whisper.android](examples/whisper.android) | | Android mobile application using whisper.cpp |
|
| [whisper.android](examples/whisper.android) | | Android mobile application using whisper.cpp |
|
||||||
|
@ -1,142 +0,0 @@
|
|||||||
//
|
|
||||||
// CoremlEncoder.h
|
|
||||||
//
|
|
||||||
// This file was automatically generated and should not be edited.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreML/CoreML.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <os/log.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
|
|
||||||
/// Model Prediction Input Type
|
|
||||||
API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0)) __attribute__((visibility("hidden")))
|
|
||||||
@interface CoremlEncoderInput : NSObject<MLFeatureProvider>
|
|
||||||
|
|
||||||
/// melSegment as 1 × 80 × 3000 3-dimensional array of floats
|
|
||||||
@property (readwrite, nonatomic, strong) MLMultiArray * melSegment;
|
|
||||||
- (instancetype)init NS_UNAVAILABLE;
|
|
||||||
- (instancetype)initWithMelSegment:(MLMultiArray *)melSegment NS_DESIGNATED_INITIALIZER;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
/// Model Prediction Output Type
|
|
||||||
API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0)) __attribute__((visibility("hidden")))
|
|
||||||
@interface CoremlEncoderOutput : NSObject<MLFeatureProvider>
|
|
||||||
|
|
||||||
/// output as multidimensional array of floats
|
|
||||||
@property (readwrite, nonatomic, strong) MLMultiArray * output;
|
|
||||||
- (instancetype)init NS_UNAVAILABLE;
|
|
||||||
- (instancetype)initWithOutput:(MLMultiArray *)output NS_DESIGNATED_INITIALIZER;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
/// Class for model loading and prediction
|
|
||||||
API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0)) __attribute__((visibility("hidden")))
|
|
||||||
@interface CoremlEncoder : NSObject
|
|
||||||
@property (readonly, nonatomic, nullable) MLModel * model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
URL of the underlying .mlmodelc directory.
|
|
||||||
*/
|
|
||||||
+ (nullable NSURL *)URLOfModelInThisBundle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize CoremlEncoder instance from an existing MLModel object.
|
|
||||||
|
|
||||||
Usually the application does not use this initializer unless it makes a subclass of CoremlEncoder.
|
|
||||||
Such application may want to use `-[MLModel initWithContentsOfURL:configuration:error:]` and `+URLOfModelInThisBundle` to create a MLModel object to pass-in.
|
|
||||||
*/
|
|
||||||
- (instancetype)initWithMLModel:(MLModel *)model NS_DESIGNATED_INITIALIZER;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize CoremlEncoder instance with the model in this bundle.
|
|
||||||
*/
|
|
||||||
- (nullable instancetype)init;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize CoremlEncoder instance with the model in this bundle.
|
|
||||||
|
|
||||||
@param configuration The model configuration object
|
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
|
||||||
*/
|
|
||||||
- (nullable instancetype)initWithConfiguration:(MLModelConfiguration *)configuration error:(NSError * _Nullable __autoreleasing * _Nullable)error;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize CoremlEncoder instance from the model URL.
|
|
||||||
|
|
||||||
@param modelURL URL to the .mlmodelc directory for CoremlEncoder.
|
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
|
||||||
*/
|
|
||||||
- (nullable instancetype)initWithContentsOfURL:(NSURL *)modelURL error:(NSError * _Nullable __autoreleasing * _Nullable)error;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize CoremlEncoder instance from the model URL.
|
|
||||||
|
|
||||||
@param modelURL URL to the .mlmodelc directory for CoremlEncoder.
|
|
||||||
@param configuration The model configuration object
|
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
|
||||||
*/
|
|
||||||
- (nullable instancetype)initWithContentsOfURL:(NSURL *)modelURL configuration:(MLModelConfiguration *)configuration error:(NSError * _Nullable __autoreleasing * _Nullable)error;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Construct CoremlEncoder instance asynchronously with configuration.
|
|
||||||
Model loading may take time when the model content is not immediately available (e.g. encrypted model). Use this factory method especially when the caller is on the main thread.
|
|
||||||
|
|
||||||
@param configuration The model configuration
|
|
||||||
@param handler When the model load completes successfully or unsuccessfully, the completion handler is invoked with a valid CoremlEncoder instance or NSError object.
|
|
||||||
*/
|
|
||||||
+ (void)loadWithConfiguration:(MLModelConfiguration *)configuration completionHandler:(void (^)(CoremlEncoder * _Nullable model, NSError * _Nullable error))handler API_AVAILABLE(macos(11.0), ios(14.0), watchos(7.0), tvos(14.0)) __attribute__((visibility("hidden")));
|
|
||||||
|
|
||||||
/**
|
|
||||||
Construct CoremlEncoder instance asynchronously with URL of .mlmodelc directory and optional configuration.
|
|
||||||
|
|
||||||
Model loading may take time when the model content is not immediately available (e.g. encrypted model). Use this factory method especially when the caller is on the main thread.
|
|
||||||
|
|
||||||
@param modelURL The model URL.
|
|
||||||
@param configuration The model configuration
|
|
||||||
@param handler When the model load completes successfully or unsuccessfully, the completion handler is invoked with a valid CoremlEncoder instance or NSError object.
|
|
||||||
*/
|
|
||||||
+ (void)loadContentsOfURL:(NSURL *)modelURL configuration:(MLModelConfiguration *)configuration completionHandler:(void (^)(CoremlEncoder * _Nullable model, NSError * _Nullable error))handler API_AVAILABLE(macos(11.0), ios(14.0), watchos(7.0), tvos(14.0)) __attribute__((visibility("hidden")));
|
|
||||||
|
|
||||||
/**
|
|
||||||
Make a prediction using the standard interface
|
|
||||||
@param input an instance of CoremlEncoderInput to predict from
|
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
|
||||||
@return the prediction as CoremlEncoderOutput
|
|
||||||
*/
|
|
||||||
- (nullable CoremlEncoderOutput *)predictionFromFeatures:(CoremlEncoderInput *)input error:(NSError * _Nullable __autoreleasing * _Nullable)error;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Make a prediction using the standard interface
|
|
||||||
@param input an instance of CoremlEncoderInput to predict from
|
|
||||||
@param options prediction options
|
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
|
||||||
@return the prediction as CoremlEncoderOutput
|
|
||||||
*/
|
|
||||||
- (nullable CoremlEncoderOutput *)predictionFromFeatures:(CoremlEncoderInput *)input options:(MLPredictionOptions *)options error:(NSError * _Nullable __autoreleasing * _Nullable)error;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Make a prediction using the convenience interface
|
|
||||||
@param melSegment as 1 × 80 × 3000 3-dimensional array of floats:
|
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
|
||||||
@return the prediction as CoremlEncoderOutput
|
|
||||||
*/
|
|
||||||
- (nullable CoremlEncoderOutput *)predictionFromMelSegment:(MLMultiArray *)melSegment error:(NSError * _Nullable __autoreleasing * _Nullable)error;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Batch prediction
|
|
||||||
@param inputArray array of CoremlEncoderInput instances to obtain predictions from
|
|
||||||
@param options prediction options
|
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
|
||||||
@return the predictions as NSArray<CoremlEncoderOutput *>
|
|
||||||
*/
|
|
||||||
- (nullable NSArray<CoremlEncoderOutput *> *)predictionsFromInputs:(NSArray<CoremlEncoderInput*> *)inputArray options:(MLPredictionOptions *)options error:(NSError * _Nullable __autoreleasing * _Nullable)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,197 +0,0 @@
|
|||||||
//
|
|
||||||
// CoremlEncoder.m
|
|
||||||
//
|
|
||||||
// This file was automatically generated and should not be edited.
|
|
||||||
//
|
|
||||||
|
|
||||||
#if !__has_feature(objc_arc)
|
|
||||||
#error This file must be compiled with automatic reference counting enabled (-fobjc-arc)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#import "whisper-encoder-impl.h"
|
|
||||||
|
|
||||||
@implementation CoremlEncoderInput
|
|
||||||
|
|
||||||
- (instancetype)initWithMelSegment:(MLMultiArray *)melSegment {
|
|
||||||
self = [super init];
|
|
||||||
if (self) {
|
|
||||||
_melSegment = melSegment;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSSet<NSString *> *)featureNames {
|
|
||||||
return [NSSet setWithArray:@[@"melSegment"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (nullable MLFeatureValue *)featureValueForName:(NSString *)featureName {
|
|
||||||
if ([featureName isEqualToString:@"melSegment"]) {
|
|
||||||
return [MLFeatureValue featureValueWithMultiArray:self.melSegment];
|
|
||||||
}
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CoremlEncoderOutput
|
|
||||||
|
|
||||||
- (instancetype)initWithOutput:(MLMultiArray *)output {
|
|
||||||
self = [super init];
|
|
||||||
if (self) {
|
|
||||||
_output = output;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSSet<NSString *> *)featureNames {
|
|
||||||
return [NSSet setWithArray:@[@"output"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (nullable MLFeatureValue *)featureValueForName:(NSString *)featureName {
|
|
||||||
if ([featureName isEqualToString:@"output"]) {
|
|
||||||
return [MLFeatureValue featureValueWithMultiArray:self.output];
|
|
||||||
}
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CoremlEncoder
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
URL of the underlying .mlmodelc directory.
|
|
||||||
*/
|
|
||||||
+ (nullable NSURL *)URLOfModelInThisBundle {
|
|
||||||
NSString *assetPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"CoremlEncoder" ofType:@"mlmodelc"];
|
|
||||||
if (nil == assetPath) { os_log_error(OS_LOG_DEFAULT, "Could not load CoremlEncoder.mlmodelc in the bundle resource"); return nil; }
|
|
||||||
return [NSURL fileURLWithPath:assetPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize CoremlEncoder instance from an existing MLModel object.
|
|
||||||
|
|
||||||
Usually the application does not use this initializer unless it makes a subclass of CoremlEncoder.
|
|
||||||
Such application may want to use `-[MLModel initWithContentsOfURL:configuration:error:]` and `+URLOfModelInThisBundle` to create a MLModel object to pass-in.
|
|
||||||
*/
|
|
||||||
- (instancetype)initWithMLModel:(MLModel *)model {
|
|
||||||
self = [super init];
|
|
||||||
if (!self) { return nil; }
|
|
||||||
_model = model;
|
|
||||||
if (_model == nil) { return nil; }
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize CoremlEncoder instance with the model in this bundle.
|
|
||||||
*/
|
|
||||||
- (nullable instancetype)init {
|
|
||||||
return [self initWithContentsOfURL:(NSURL * _Nonnull)self.class.URLOfModelInThisBundle error:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize CoremlEncoder instance with the model in this bundle.
|
|
||||||
|
|
||||||
@param configuration The model configuration object
|
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
|
||||||
*/
|
|
||||||
- (nullable instancetype)initWithConfiguration:(MLModelConfiguration *)configuration error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
|
||||||
return [self initWithContentsOfURL:(NSURL * _Nonnull)self.class.URLOfModelInThisBundle configuration:configuration error:error];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize CoremlEncoder instance from the model URL.
|
|
||||||
|
|
||||||
@param modelURL URL to the .mlmodelc directory for CoremlEncoder.
|
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
|
||||||
*/
|
|
||||||
- (nullable instancetype)initWithContentsOfURL:(NSURL *)modelURL error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
|
||||||
MLModel *model = [MLModel modelWithContentsOfURL:modelURL error:error];
|
|
||||||
if (model == nil) { return nil; }
|
|
||||||
return [self initWithMLModel:model];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Initialize CoremlEncoder instance from the model URL.
|
|
||||||
|
|
||||||
@param modelURL URL to the .mlmodelc directory for CoremlEncoder.
|
|
||||||
@param configuration The model configuration object
|
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
|
||||||
*/
|
|
||||||
- (nullable instancetype)initWithContentsOfURL:(NSURL *)modelURL configuration:(MLModelConfiguration *)configuration error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
|
||||||
MLModel *model = [MLModel modelWithContentsOfURL:modelURL configuration:configuration error:error];
|
|
||||||
if (model == nil) { return nil; }
|
|
||||||
return [self initWithMLModel:model];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Construct CoremlEncoder instance asynchronously with configuration.
|
|
||||||
Model loading may take time when the model content is not immediately available (e.g. encrypted model). Use this factory method especially when the caller is on the main thread.
|
|
||||||
|
|
||||||
@param configuration The model configuration
|
|
||||||
@param handler When the model load completes successfully or unsuccessfully, the completion handler is invoked with a valid CoremlEncoder instance or NSError object.
|
|
||||||
*/
|
|
||||||
+ (void)loadWithConfiguration:(MLModelConfiguration *)configuration completionHandler:(void (^)(CoremlEncoder * _Nullable model, NSError * _Nullable error))handler {
|
|
||||||
[self loadContentsOfURL:(NSURL * _Nonnull)[self URLOfModelInThisBundle]
|
|
||||||
configuration:configuration
|
|
||||||
completionHandler:handler];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Construct CoremlEncoder instance asynchronously with URL of .mlmodelc directory and optional configuration.
|
|
||||||
|
|
||||||
Model loading may take time when the model content is not immediately available (e.g. encrypted model). Use this factory method especially when the caller is on the main thread.
|
|
||||||
|
|
||||||
@param modelURL The model URL.
|
|
||||||
@param configuration The model configuration
|
|
||||||
@param handler When the model load completes successfully or unsuccessfully, the completion handler is invoked with a valid CoremlEncoder instance or NSError object.
|
|
||||||
*/
|
|
||||||
+ (void)loadContentsOfURL:(NSURL *)modelURL configuration:(MLModelConfiguration *)configuration completionHandler:(void (^)(CoremlEncoder * _Nullable model, NSError * _Nullable error))handler {
|
|
||||||
[MLModel loadContentsOfURL:modelURL
|
|
||||||
configuration:configuration
|
|
||||||
completionHandler:^(MLModel *model, NSError *error) {
|
|
||||||
if (model != nil) {
|
|
||||||
CoremlEncoder *typedModel = [[CoremlEncoder alloc] initWithMLModel:model];
|
|
||||||
handler(typedModel, nil);
|
|
||||||
} else {
|
|
||||||
handler(nil, error);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (nullable CoremlEncoderOutput *)predictionFromFeatures:(CoremlEncoderInput *)input error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
|
||||||
return [self predictionFromFeatures:input options:[[MLPredictionOptions alloc] init] error:error];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (nullable CoremlEncoderOutput *)predictionFromFeatures:(CoremlEncoderInput *)input options:(MLPredictionOptions *)options error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
|
||||||
id<MLFeatureProvider> outFeatures = [self.model predictionFromFeatures:input options:options error:error];
|
|
||||||
if (!outFeatures) { return nil; }
|
|
||||||
return [[CoremlEncoderOutput alloc] initWithOutput:(MLMultiArray *)[outFeatures featureValueForName:@"output"].multiArrayValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (nullable CoremlEncoderOutput *)predictionFromMelSegment:(MLMultiArray *)melSegment error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
|
||||||
CoremlEncoderInput *input_ = [[CoremlEncoderInput alloc] initWithMelSegment:melSegment];
|
|
||||||
return [self predictionFromFeatures:input_ error:error];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (nullable NSArray<CoremlEncoderOutput *> *)predictionsFromInputs:(NSArray<CoremlEncoderInput*> *)inputArray options:(MLPredictionOptions *)options error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
|
||||||
id<MLBatchProvider> inBatch = [[MLArrayBatchProvider alloc] initWithFeatureProviderArray:inputArray];
|
|
||||||
id<MLBatchProvider> outBatch = [self.model predictionsFromBatch:inBatch options:options error:error];
|
|
||||||
if (!outBatch) { return nil; }
|
|
||||||
NSMutableArray<CoremlEncoderOutput*> *results = [NSMutableArray arrayWithCapacity:(NSUInteger)outBatch.count];
|
|
||||||
for (NSInteger i = 0; i < outBatch.count; i++) {
|
|
||||||
id<MLFeatureProvider> resultProvider = [outBatch featuresAtIndex:i];
|
|
||||||
CoremlEncoderOutput * result = [[CoremlEncoderOutput alloc] initWithOutput:(MLMultiArray *)[resultProvider featureValueForName:@"output"].multiArrayValue];
|
|
||||||
[results addObject:result];
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,22 +0,0 @@
|
|||||||
// Wrapper of the Core ML Whisper Encoder model
|
|
||||||
//
|
|
||||||
// Code is derived from the work of Github user @wangchou
|
|
||||||
// ref: https://github.com/wangchou/callCoreMLFromCpp
|
|
||||||
|
|
||||||
#if __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct whisper_coreml_context;
|
|
||||||
|
|
||||||
struct whisper_coreml_context * whisper_coreml_init(const char * path_model);
|
|
||||||
void whisper_coreml_free(struct whisper_coreml_context * ctx);
|
|
||||||
|
|
||||||
void whisper_coreml_encode(
|
|
||||||
const whisper_coreml_context * ctx,
|
|
||||||
float * mel,
|
|
||||||
float * out);
|
|
||||||
|
|
||||||
#if __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,61 +0,0 @@
|
|||||||
#import "coreml/whisper-encoder.h"
|
|
||||||
#import "coreml/whisper-encoder-impl.h"
|
|
||||||
|
|
||||||
#import <CoreML/CoreML.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#if __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct whisper_coreml_context {
|
|
||||||
const void * data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct whisper_coreml_context * whisper_coreml_init(const char * path_model) {
|
|
||||||
NSString * path_model_str = [[NSString alloc] initWithUTF8String:path_model];
|
|
||||||
|
|
||||||
NSURL * url_model = [NSURL fileURLWithPath: path_model_str];
|
|
||||||
|
|
||||||
const void * data = CFBridgingRetain([[CoremlEncoder alloc] initWithContentsOfURL:url_model error:nil]);
|
|
||||||
|
|
||||||
if (data == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
whisper_coreml_context * ctx = new whisper_coreml_context;
|
|
||||||
|
|
||||||
ctx->data = data;
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void whisper_coreml_free(struct whisper_coreml_context * ctx) {
|
|
||||||
CFRelease(ctx->data);
|
|
||||||
delete ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void whisper_coreml_encode(
|
|
||||||
const whisper_coreml_context * ctx,
|
|
||||||
float * mel,
|
|
||||||
float * out) {
|
|
||||||
MLMultiArray * inMultiArray = [
|
|
||||||
[MLMultiArray alloc] initWithDataPointer: mel
|
|
||||||
shape: @[@1, @80, @3000]
|
|
||||||
dataType: MLMultiArrayDataTypeFloat32
|
|
||||||
strides: @[@(240000), @(3000), @1]
|
|
||||||
deallocator: nil
|
|
||||||
error: nil
|
|
||||||
];
|
|
||||||
|
|
||||||
CoremlEncoderOutput * outCoreML = [(__bridge id) ctx->data predictionFromMelSegment:inMultiArray error:nil];
|
|
||||||
|
|
||||||
MLMultiArray * outMA = outCoreML.output;
|
|
||||||
|
|
||||||
memcpy(out, outMA.dataPointer, outMA.count * sizeof(float));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -63,5 +63,5 @@ else()
|
|||||||
add_subdirectory(command)
|
add_subdirectory(command)
|
||||||
add_subdirectory(bench)
|
add_subdirectory(bench)
|
||||||
add_subdirectory(talk)
|
add_subdirectory(talk)
|
||||||
add_subdirectory(talk.llama)
|
add_subdirectory(talk-llama)
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
const path = require('path');
|
const path = require("path");
|
||||||
const { whisper } = require(path.join(__dirname, '../../../build/Release/whisper-addon'));
|
const { whisper } = require(path.join(
|
||||||
|
__dirname,
|
||||||
|
"../../../build/Release/whisper-addon"
|
||||||
|
));
|
||||||
|
const { promisify } = require("util");
|
||||||
|
|
||||||
|
const whisperAsync = promisify(whisper);
|
||||||
|
|
||||||
const whisperParamsMock = {
|
const whisperParamsMock = {
|
||||||
language: 'en',
|
language: "en",
|
||||||
model: path.join(__dirname, '../../../models/ggml-base.en.bin'),
|
model: path.join(__dirname, "../../../models/ggml-base.en.bin"),
|
||||||
fname_inp: path.join(__dirname, '../../../samples/jfk.wav'),
|
fname_inp: path.join(__dirname, "../../../samples/jfk.wav"),
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("Run whisper.node", () => {
|
describe("Run whisper.node", () => {
|
||||||
|
test("it should receive a non-empty value", async () => {
|
||||||
|
let result = await whisperAsync(whisperParamsMock);
|
||||||
|
|
||||||
test("it should receive a non-empty value", () => {
|
expect(result.length).toBeGreaterThan(0);
|
||||||
expect(whisper(whisperParamsMock).length).toBeGreaterThan(0);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -160,22 +160,6 @@ int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial prompt
|
|
||||||
std::vector<whisper_token> prompt_tokens;
|
|
||||||
|
|
||||||
if (!params.prompt.empty()) {
|
|
||||||
prompt_tokens.resize(1024);
|
|
||||||
prompt_tokens.resize(whisper_tokenize(ctx, params.prompt.c_str(), prompt_tokens.data(), prompt_tokens.size()));
|
|
||||||
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fprintf(stderr, "initial prompt: '%s'\n", params.prompt.c_str());
|
|
||||||
fprintf(stderr, "initial tokens: [ ");
|
|
||||||
for (int i = 0; i < (int) prompt_tokens.size(); ++i) {
|
|
||||||
fprintf(stderr, "%d ", prompt_tokens[i]);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "]\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int f = 0; f < (int) params.fname_inp.size(); ++f) {
|
for (int f = 0; f < (int) params.fname_inp.size(); ++f) {
|
||||||
const auto fname_inp = params.fname_inp[f];
|
const auto fname_inp = params.fname_inp[f];
|
||||||
const auto fname_out = f < (int)params.fname_out.size() && !params.fname_out[f].empty() ? params.fname_out[f] : params.fname_inp[f];
|
const auto fname_out = f < (int)params.fname_out.size() && !params.fname_out[f].empty() ? params.fname_out[f] : params.fname_inp[f];
|
||||||
@ -243,8 +227,7 @@ int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
|||||||
wparams.greedy.best_of = params.best_of;
|
wparams.greedy.best_of = params.best_of;
|
||||||
wparams.beam_search.beam_size = params.beam_size;
|
wparams.beam_search.beam_size = params.beam_size;
|
||||||
|
|
||||||
wparams.prompt_tokens = prompt_tokens.empty() ? nullptr : prompt_tokens.data();
|
wparams.initial_prompt = params.prompt.c_str();
|
||||||
wparams.prompt_n_tokens = prompt_tokens.empty() ? 0 : prompt_tokens.size();
|
|
||||||
|
|
||||||
whisper_print_user_data user_data = { ¶ms, &pcmf32s };
|
whisper_print_user_data user_data = { ¶ms, &pcmf32s };
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
// Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9]
|
// Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9]
|
||||||
// Lowest is red, middle is yellow, highest is green.
|
// Lowest is red, middle is yellow, highest is green.
|
||||||
@ -371,6 +372,39 @@ bool output_csv(struct whisper_context * ctx, const char * fname) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *escape_double_quotes(const char *str) {
|
||||||
|
if (str == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t escaped_length = strlen(str) + 1;
|
||||||
|
|
||||||
|
for (size_t i = 0; str[i] != '\0'; i++) {
|
||||||
|
if (str[i] == '"') {
|
||||||
|
escaped_length++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *escaped = (char *)calloc(escaped_length, 1); // pre-zeroed
|
||||||
|
if (escaped == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
for (size_t i = 0; str[i] != '\0'; i++) {
|
||||||
|
if (str[i] == '"') {
|
||||||
|
escaped[pos++] = '\\';
|
||||||
|
escaped[pos++] = '"';
|
||||||
|
} else {
|
||||||
|
escaped[pos++] = str[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to set zero due to calloc() being used prior
|
||||||
|
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
|
||||||
bool output_json(struct whisper_context * ctx, const char * fname, const whisper_params & params) {
|
bool output_json(struct whisper_context * ctx, const char * fname, const whisper_params & params) {
|
||||||
std::ofstream fout(fname);
|
std::ofstream fout(fname);
|
||||||
int indent = 0;
|
int indent = 0;
|
||||||
@ -414,7 +448,9 @@ bool output_json(struct whisper_context * ctx, const char * fname, const whisper
|
|||||||
|
|
||||||
auto value_s = [&](const char *name, const char *val, bool end = false) {
|
auto value_s = [&](const char *name, const char *val, bool end = false) {
|
||||||
start_value(name);
|
start_value(name);
|
||||||
fout << "\"" << val << (end ? "\"\n" : "\",\n");
|
char * val_escaped = escape_double_quotes(val);
|
||||||
|
fout << "\"" << val_escaped << (end ? "\"\n" : "\",\n");
|
||||||
|
free(val_escaped);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto end_value = [&](bool end = false) {
|
auto end_value = [&](bool end = false) {
|
||||||
@ -455,7 +491,7 @@ bool output_json(struct whisper_context * ctx, const char * fname, const whisper
|
|||||||
value_i("ctx", whisper_model_n_text_ctx(ctx));
|
value_i("ctx", whisper_model_n_text_ctx(ctx));
|
||||||
value_i("state", whisper_model_n_text_state(ctx));
|
value_i("state", whisper_model_n_text_state(ctx));
|
||||||
value_i("head", whisper_model_n_text_head(ctx));
|
value_i("head", whisper_model_n_text_head(ctx));
|
||||||
value_i("leyer", whisper_model_n_text_layer(ctx), true);
|
value_i("layer", whisper_model_n_text_layer(ctx), true);
|
||||||
end_obj();
|
end_obj();
|
||||||
value_i("mels", whisper_model_n_mels(ctx));
|
value_i("mels", whisper_model_n_mels(ctx));
|
||||||
value_i("f16", whisper_model_f16(ctx), true);
|
value_i("f16", whisper_model_f16(ctx), true);
|
||||||
@ -477,7 +513,7 @@ bool output_json(struct whisper_context * ctx, const char * fname, const whisper
|
|||||||
const int64_t t1 = whisper_full_get_segment_t1(ctx, i);
|
const int64_t t1 = whisper_full_get_segment_t1(ctx, i);
|
||||||
|
|
||||||
start_obj();
|
start_obj();
|
||||||
start_obj("timestanps");
|
start_obj("timestamps");
|
||||||
value_s("from", to_timestamp(t0, true).c_str());
|
value_s("from", to_timestamp(t0, true).c_str());
|
||||||
value_s("to", to_timestamp(t1, true).c_str(), true);
|
value_s("to", to_timestamp(t1, true).c_str(), true);
|
||||||
end_obj();
|
end_obj();
|
||||||
@ -639,22 +675,6 @@ int main(int argc, char ** argv) {
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial prompt
|
|
||||||
std::vector<whisper_token> prompt_tokens;
|
|
||||||
|
|
||||||
if (!params.prompt.empty()) {
|
|
||||||
prompt_tokens.resize(1024);
|
|
||||||
prompt_tokens.resize(whisper_tokenize(ctx, params.prompt.c_str(), prompt_tokens.data(), prompt_tokens.size()));
|
|
||||||
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fprintf(stderr, "initial prompt: '%s'\n", params.prompt.c_str());
|
|
||||||
fprintf(stderr, "initial tokens: [ ");
|
|
||||||
for (int i = 0; i < (int) prompt_tokens.size(); ++i) {
|
|
||||||
fprintf(stderr, "%d ", prompt_tokens[i]);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "]\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int f = 0; f < (int) params.fname_inp.size(); ++f) {
|
for (int f = 0; f < (int) params.fname_inp.size(); ++f) {
|
||||||
const auto fname_inp = params.fname_inp[f];
|
const auto fname_inp = params.fname_inp[f];
|
||||||
const auto fname_out = f < (int) params.fname_out.size() && !params.fname_out[f].empty() ? params.fname_out[f] : params.fname_inp[f];
|
const auto fname_out = f < (int) params.fname_out.size() && !params.fname_out[f].empty() ? params.fname_out[f] : params.fname_inp[f];
|
||||||
@ -718,8 +738,7 @@ int main(int argc, char ** argv) {
|
|||||||
|
|
||||||
wparams.speed_up = params.speed_up;
|
wparams.speed_up = params.speed_up;
|
||||||
|
|
||||||
wparams.prompt_tokens = prompt_tokens.empty() ? nullptr : prompt_tokens.data();
|
wparams.initial_prompt = params.prompt.c_str();
|
||||||
wparams.prompt_n_tokens = prompt_tokens.empty() ? 0 : prompt_tokens.size();
|
|
||||||
|
|
||||||
wparams.greedy.best_of = params.best_of;
|
wparams.greedy.best_of = params.best_of;
|
||||||
wparams.beam_search.beam_size = params.beam_size;
|
wparams.beam_search.beam_size = params.beam_size;
|
||||||
|
16
examples/talk-llama/CMakeLists.txt
Normal file
16
examples/talk-llama/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
if (WHISPER_SUPPORT_SDL2)
|
||||||
|
# talk-llama
|
||||||
|
set(TARGET talk-llama)
|
||||||
|
#add_executable(${TARGET} talk-llama.cpp llama.cpp)
|
||||||
|
#target_include_directories(${TARGET} PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||||
|
#target_link_libraries(${TARGET} PRIVATE common common-sdl whisper ${SDL2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
|
||||||
|
# TODO: this is temporary
|
||||||
|
# need to export ggml symbols for MSVC, but too lazy ..
|
||||||
|
add_executable(${TARGET} talk-llama.cpp llama.cpp ../common.cpp ../common-sdl.cpp ../../ggml.c ../../whisper.cpp)
|
||||||
|
|
||||||
|
target_include_directories(${TARGET} PRIVATE ${SDL2_INCLUDE_DIRS} ../../)
|
||||||
|
target_link_libraries(${TARGET} PRIVATE ${SDL2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
|
||||||
|
include(DefaultTargetOptions)
|
||||||
|
endif ()
|
36
examples/talk-llama/README.md
Normal file
36
examples/talk-llama/README.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# talk-llama
|
||||||
|
|
||||||
|
Talk with an LLaMA AI in your terminal
|
||||||
|
|
||||||
|
[Demo Talk](https://user-images.githubusercontent.com/1991296/228024237-848f998c-c334-46a6-bef8-3271590da83b.mp4)
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
The `talk-llama` tool depends on SDL2 library to capture audio from the microphone. You can build it like this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install SDL2 on Linux
|
||||||
|
sudo apt-get install libsdl2-dev
|
||||||
|
|
||||||
|
# Install SDL2 on Mac OS
|
||||||
|
brew install sdl2
|
||||||
|
|
||||||
|
# Build the "talk-llama" executable
|
||||||
|
make talk-llama
|
||||||
|
|
||||||
|
# Run it
|
||||||
|
./talk-llama -mw ./models/ggml-small.en.bin -ml ../llama.cpp/models/13B/ggml-model-q4_0.bin -p "Georgi" -t 8
|
||||||
|
```
|
||||||
|
|
||||||
|
- The `-mw` argument specifies the Whisper model that you would like to use. Recommended `base` or `small` for real-time experience
|
||||||
|
- The `-ml` argument specifies the LLaMA model that you would like to use. Read the instructions in https://github.com/ggerganov/llama.cpp for information about how to obtain a `ggml` compatible LLaMA model
|
||||||
|
|
||||||
|
## TTS
|
||||||
|
|
||||||
|
For best experience, this example needs a TTS tool to convert the generated text responses to voice.
|
||||||
|
You can use any TTS engine that you would like - simply edit the [speak.sh](speak.sh) script to your needs.
|
||||||
|
By default, it is configured to use MacOS's `say`, but you can use whatever you wish.
|
||||||
|
|
||||||
|
## Discussion
|
||||||
|
|
||||||
|
If you have any feedback, please let "us" know in the following discussion: https://github.com/ggerganov/whisper.cpp/discussions/672?converting=1
|
@ -12,6 +12,19 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(_POSIX_MAPPED_FILES)
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define Min(X, Y) ((Y) > (X) ? (X) : (Y))
|
||||||
|
#define Max(X, Y) ((Y) < (X) ? (X) : (Y))
|
||||||
|
|
||||||
#define LLAMA_USE_SCRATCH
|
#define LLAMA_USE_SCRATCH
|
||||||
#define LLAMA_MAX_SCRATCH_BUFFERS 16
|
#define LLAMA_MAX_SCRATCH_BUFFERS 16
|
||||||
|
|
||||||
@ -142,6 +155,10 @@ struct llama_model {
|
|||||||
// the model memory buffer
|
// the model memory buffer
|
||||||
std::vector<uint8_t> buf;
|
std::vector<uint8_t> buf;
|
||||||
|
|
||||||
|
// model memory mapped file
|
||||||
|
void * mm_addr = NULL;
|
||||||
|
uint64_t mm_length = 0;
|
||||||
|
|
||||||
// tensors
|
// tensors
|
||||||
int n_loaded;
|
int n_loaded;
|
||||||
std::unordered_map<std::string, struct ggml_tensor *> tensors;
|
std::unordered_map<std::string, struct ggml_tensor *> tensors;
|
||||||
@ -165,6 +182,7 @@ struct llama_context {
|
|||||||
|
|
||||||
int64_t t_load_us = 0;
|
int64_t t_load_us = 0;
|
||||||
int64_t t_start_us = 0;
|
int64_t t_start_us = 0;
|
||||||
|
bool has_evaluated_once = false;
|
||||||
|
|
||||||
int64_t t_sample_us = 0;
|
int64_t t_sample_us = 0;
|
||||||
int64_t t_eval_us = 0;
|
int64_t t_eval_us = 0;
|
||||||
@ -206,7 +224,7 @@ struct llama_context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buf_last >= 0) {
|
if (buf_last >= 0) {
|
||||||
buf_max_size[buf_last] = std::max(buf_max_size[buf_last], last_size);
|
buf_max_size[buf_last] = Max(buf_max_size[buf_last], last_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf_last = i;
|
buf_last = i;
|
||||||
@ -246,6 +264,7 @@ static bool kv_cache_init(
|
|||||||
struct ggml_init_params params;
|
struct ggml_init_params params;
|
||||||
params.mem_size = cache.buf.size();
|
params.mem_size = cache.buf.size();
|
||||||
params.mem_buffer = cache.buf.data();
|
params.mem_buffer = cache.buf.data();
|
||||||
|
params.no_alloc = false;
|
||||||
|
|
||||||
cache.ctx = ggml_init(params);
|
cache.ctx = ggml_init(params);
|
||||||
|
|
||||||
@ -288,6 +307,58 @@ struct llama_context_params llama_context_default_params() {
|
|||||||
// model loading
|
// model loading
|
||||||
//
|
//
|
||||||
|
|
||||||
|
static void *mmap_file(const char *fname, uint64_t *mm_length) {
|
||||||
|
#if defined(_WIN32) && !defined(_POSIX_MAPPED_FILES)
|
||||||
|
HANDLE hFile = CreateFileA(fname,
|
||||||
|
GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
|
||||||
|
NULL);
|
||||||
|
if (hFile == INVALID_HANDLE_VALUE) return 0;
|
||||||
|
LARGE_INTEGER fileSize;
|
||||||
|
fileSize.QuadPart = -1;
|
||||||
|
GetFileSizeEx(hFile, &fileSize);
|
||||||
|
int64_t length = fileSize.QuadPart;
|
||||||
|
HANDLE hMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||||
|
CloseHandle(hFile);
|
||||||
|
if (!hMapping) return 0;
|
||||||
|
void *addr = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
|
||||||
|
CloseHandle(hMapping);
|
||||||
|
if (!addr) return 0;
|
||||||
|
#else
|
||||||
|
int fd = open(fname, O_RDONLY);
|
||||||
|
if (fd == -1) return 0;
|
||||||
|
int64_t length = lseek(fd, 0, SEEK_END);
|
||||||
|
void *addr = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
close(fd);
|
||||||
|
if (addr == MAP_FAILED) return 0;
|
||||||
|
#endif
|
||||||
|
*mm_length = length;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void munmap_file(void * addr, size_t length) {
|
||||||
|
#if defined(_WIN32) && !defined(_POSIX_MAPPED_FILES)
|
||||||
|
UnmapViewOfFile(addr);
|
||||||
|
#else
|
||||||
|
munmap(addr, length);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool report_bad_magic(const char *path, uint32_t got, uint32_t want) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: invalid model file (bad magic [got %#x want %#x])\n"
|
||||||
|
"\tyou most likely need to regenerate your ggml files\n"
|
||||||
|
"\tthe benefit is you'll get 10-100x faster load times\n"
|
||||||
|
"\tsee https://github.com/ggerganov/llama.cpp/issues/91\n"
|
||||||
|
"\tuse convert-pth-to-ggml.py to regenerate from original pth\n"
|
||||||
|
"\tuse migrate-ggml-2023-03-30-pr613.py if you deleted originals\n",
|
||||||
|
path, got, want);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool llama_model_load(
|
static bool llama_model_load(
|
||||||
const std::string & fname,
|
const std::string & fname,
|
||||||
llama_context & lctx,
|
llama_context & lctx,
|
||||||
@ -299,34 +370,35 @@ static bool llama_model_load(
|
|||||||
void *progress_callback_user_data) {
|
void *progress_callback_user_data) {
|
||||||
fprintf(stderr, "%s: loading model from '%s' - please wait ...\n", __func__, fname.c_str());
|
fprintf(stderr, "%s: loading model from '%s' - please wait ...\n", __func__, fname.c_str());
|
||||||
|
|
||||||
const int64_t t_start_us = ggml_time_us();
|
lctx.t_start_us = ggml_time_us();
|
||||||
|
|
||||||
lctx.t_start_us = t_start_us;
|
|
||||||
|
|
||||||
std::vector<char> f_buf(1024*1024);
|
|
||||||
|
|
||||||
auto & model = lctx.model;
|
auto & model = lctx.model;
|
||||||
auto & vocab = lctx.vocab;
|
auto & vocab = lctx.vocab;
|
||||||
|
|
||||||
auto fin = std::ifstream(fname, std::ios::binary);
|
auto fin = std::ifstream(fname, std::ios::binary);
|
||||||
fin.rdbuf()->pubsetbuf(f_buf.data(), f_buf.size());
|
|
||||||
if (!fin) {
|
if (!fin) {
|
||||||
fprintf(stderr, "%s: failed to open '%s'\n", __func__, fname.c_str());
|
fprintf(stderr, "%s: failed to open '%s'\n", __func__, fname.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<char> f_buf(1024*1024);
|
||||||
|
fin.rdbuf()->pubsetbuf(f_buf.data(), f_buf.size());
|
||||||
|
|
||||||
|
fin.seekg(0, fin.end);
|
||||||
|
const size_t file_size = fin.tellg();
|
||||||
|
fin.seekg(0);
|
||||||
|
|
||||||
// verify magic
|
// verify magic
|
||||||
{
|
{
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
fin.read((char *) &magic, sizeof(magic));
|
fin.read((char *) &magic, sizeof(magic));
|
||||||
if (magic == LLAMA_FILE_MAGIC_UNVERSIONED) {
|
if (magic == LLAMA_FILE_MAGIC_UNVERSIONED) {
|
||||||
fprintf(stderr, "%s: invalid model file '%s' (too old, regenerate your model files!)\n",
|
fprintf(stderr, "%s: invalid model file '%s' (too old, regenerate your model files or convert them with convert-unversioned-ggml-to-ggml.py!)\n",
|
||||||
__func__, fname.c_str());
|
__func__, fname.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (magic != LLAMA_FILE_MAGIC) {
|
if (magic != LLAMA_FILE_MAGIC) {
|
||||||
fprintf(stderr, "%s: invalid model file '%s' (bad magic)\n", __func__, fname.c_str());
|
return report_bad_magic(fname.c_str(), magic, LLAMA_FILE_MAGIC);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t format_version;
|
uint32_t format_version;
|
||||||
@ -449,43 +521,24 @@ static bool llama_model_load(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// map model into memory
|
||||||
|
char *mm_addr = NULL;
|
||||||
|
model.mm_addr = mmap_file(fname.c_str(), &model.mm_length);
|
||||||
|
if (model.mm_addr == NULL) {
|
||||||
|
fprintf(stderr, "%s: failed to mmap '%s'\n", __func__, fname.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mm_addr = (char *)model.mm_addr;
|
||||||
|
fprintf(stderr, "%s: ggml map size = %6.2f MB\n", __func__, model.mm_length/(1024.0*1024.0));
|
||||||
|
|
||||||
auto & ctx = model.ctx;
|
auto & ctx = model.ctx;
|
||||||
|
|
||||||
size_t ctx_size = 0;
|
size_t ctx_size = 0;
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto & hparams = model.hparams;
|
const auto &hparams = model.hparams;
|
||||||
|
|
||||||
const int n_embd = hparams.n_embd;
|
|
||||||
const int n_layer = hparams.n_layer;
|
const int n_layer = hparams.n_layer;
|
||||||
const int n_ctx = hparams.n_ctx;
|
|
||||||
const int n_vocab = hparams.n_vocab;
|
|
||||||
|
|
||||||
ctx_size += n_embd*n_vocab*ggml_type_sizef(vtype); // tok_embeddings
|
|
||||||
|
|
||||||
ctx_size += n_embd*ggml_type_sizef(GGML_TYPE_F32); // norm
|
|
||||||
|
|
||||||
ctx_size += n_embd*n_vocab*ggml_type_sizef(vtype); // output
|
|
||||||
|
|
||||||
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // attention_norm
|
|
||||||
|
|
||||||
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // wq
|
|
||||||
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // wk
|
|
||||||
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // wv
|
|
||||||
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // wo
|
|
||||||
|
|
||||||
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ffn_norm
|
|
||||||
|
|
||||||
ctx_size += n_layer*(n_ff*n_embd*ggml_type_sizef(wtype)); // w1
|
|
||||||
ctx_size += n_layer*(n_ff*n_embd*ggml_type_sizef(wtype)); // w2
|
|
||||||
ctx_size += n_layer*(n_ff*n_embd*ggml_type_sizef(wtype)); // w3
|
|
||||||
|
|
||||||
ctx_size += n_ctx*n_layer*n_embd*ggml_type_sizef(memory_type); // memory_k
|
|
||||||
ctx_size += n_ctx*n_layer*n_embd*ggml_type_sizef(memory_type); // memory_v
|
|
||||||
|
|
||||||
ctx_size += (5 + 10*n_layer)*256; // object overhead
|
ctx_size += (5 + 10*n_layer)*256; // object overhead
|
||||||
|
fprintf(stderr, "%s: ggml ctx size = %6.2f KB\n", __func__, ctx_size/1024.0);
|
||||||
fprintf(stderr, "%s: ggml ctx size = %6.2f MB\n", __func__, ctx_size/(1024.0*1024.0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// print memory requirements
|
// print memory requirements
|
||||||
@ -495,6 +548,7 @@ static bool llama_model_load(
|
|||||||
// this is the total memory required to run the inference
|
// this is the total memory required to run the inference
|
||||||
const size_t mem_required =
|
const size_t mem_required =
|
||||||
ctx_size +
|
ctx_size +
|
||||||
|
model.mm_length +
|
||||||
MEM_REQ_SCRATCH0.at(model.type) +
|
MEM_REQ_SCRATCH0.at(model.type) +
|
||||||
MEM_REQ_SCRATCH1.at(model.type) +
|
MEM_REQ_SCRATCH1.at(model.type) +
|
||||||
MEM_REQ_EVAL.at (model.type);
|
MEM_REQ_EVAL.at (model.type);
|
||||||
@ -514,6 +568,7 @@ static bool llama_model_load(
|
|||||||
struct ggml_init_params params = {
|
struct ggml_init_params params = {
|
||||||
/*.mem_size =*/ lctx.model.buf.size(),
|
/*.mem_size =*/ lctx.model.buf.size(),
|
||||||
/*.mem_buffer =*/ lctx.model.buf.data(),
|
/*.mem_buffer =*/ lctx.model.buf.data(),
|
||||||
|
/*.no_alloc =*/ true,
|
||||||
};
|
};
|
||||||
|
|
||||||
model.ctx = ggml_init(params);
|
model.ctx = ggml_init(params);
|
||||||
@ -576,43 +631,19 @@ static bool llama_model_load(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t file_offset = fin.tellg();
|
|
||||||
|
|
||||||
fin.close();
|
|
||||||
|
|
||||||
std::vector<uint8_t> tmp;
|
std::vector<uint8_t> tmp;
|
||||||
|
|
||||||
if (progress_callback) {
|
if (progress_callback) {
|
||||||
progress_callback(0.0, progress_callback_user_data);
|
progress_callback(0.0, progress_callback_user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < n_parts; ++i) {
|
fprintf(stderr, "%s: loading tensors from '%s'\n", __func__, fname.c_str());
|
||||||
const int part_id = i;
|
|
||||||
//const int part_id = n_parts - i - 1;
|
|
||||||
|
|
||||||
std::string fname_part = fname;
|
|
||||||
if (i > 0) {
|
|
||||||
fname_part += "." + std::to_string(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "%s: loading model part %d/%d from '%s'\n", __func__, i+1, n_parts, fname_part.c_str());
|
|
||||||
|
|
||||||
fin = std::ifstream(fname_part, std::ios::binary);
|
|
||||||
fin.rdbuf()->pubsetbuf(f_buf.data(), f_buf.size());
|
|
||||||
|
|
||||||
fin.seekg(0, fin.end);
|
|
||||||
const size_t file_size = fin.tellg();
|
|
||||||
|
|
||||||
fin.seekg(file_offset);
|
|
||||||
|
|
||||||
// load weights
|
// load weights
|
||||||
{
|
{
|
||||||
size_t total_size = 0;
|
size_t total_size = 0;
|
||||||
|
|
||||||
model.n_loaded = 0;
|
model.n_loaded = 0;
|
||||||
|
|
||||||
fprintf(stderr, "%s: ", __func__);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int32_t n_dims;
|
int32_t n_dims;
|
||||||
int32_t length;
|
int32_t length;
|
||||||
@ -641,155 +672,52 @@ static bool llama_model_load(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// split_type = 0: split by columns
|
|
||||||
// split_type = 1: split by rows
|
|
||||||
int split_type = 0;
|
|
||||||
|
|
||||||
// split_type = 0:
|
|
||||||
// regex:
|
|
||||||
// - tok_embeddings.*
|
|
||||||
// - layers.*.attention.wo.weight
|
|
||||||
// - layers.*.feed_forward.w2.weight
|
|
||||||
|
|
||||||
// split_type = 1:
|
|
||||||
// regex:
|
|
||||||
// - output.*
|
|
||||||
// - layers.*.attention.wq.weight
|
|
||||||
// - layers.*.attention.wk.weight
|
|
||||||
// - layers.*.attention.wv.weight
|
|
||||||
// - layers.*.feed_forward.w1.weight
|
|
||||||
// - layers.*.feed_forward.w3.weight
|
|
||||||
if (name.find("tok_embeddings") != std::string::npos) {
|
|
||||||
split_type = 0;
|
|
||||||
} else if (name.find("layers") != std::string::npos) {
|
|
||||||
if (name.find("attention.wo.weight") != std::string::npos) {
|
|
||||||
split_type = 0;
|
|
||||||
} else if (name.find("feed_forward.w2.weight") != std::string::npos) {
|
|
||||||
split_type = 0;
|
|
||||||
} else {
|
|
||||||
split_type = 1;
|
|
||||||
}
|
|
||||||
} else if (name.find("output") != std::string::npos) {
|
|
||||||
split_type = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto tensor = model.tensors[name.data()];
|
auto tensor = model.tensors[name.data()];
|
||||||
|
|
||||||
if (n_dims == 1) {
|
|
||||||
if (ggml_nelements(tensor) != nelements) {
|
if (ggml_nelements(tensor) != nelements) {
|
||||||
fprintf(stderr, "%s: tensor '%s' has wrong size in model file\n", __func__, name.data());
|
fprintf(stderr, "%s: tensor '%s' has wrong size in model file\n", __func__, name.data());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (ggml_nelements(tensor)/n_parts != nelements) {
|
|
||||||
fprintf(stderr, "%s: tensor '%s' has wrong size in model file\n", __func__, name.data());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n_dims == 1) {
|
|
||||||
if (tensor->ne[0] != ne[0] || tensor->ne[1] != ne[1]) {
|
if (tensor->ne[0] != ne[0] || tensor->ne[1] != ne[1]) {
|
||||||
fprintf(stderr, "%s: tensor '%s' has wrong shape in model file: got [%d, %d], expected [%d, %d]\n",
|
fprintf(stderr, "%s: tensor '%s' has wrong shape in model file: got [%d, %d], expected [%d, %d]\n",
|
||||||
__func__, name.data(), tensor->ne[0], tensor->ne[1], ne[0], ne[1]);
|
__func__, name.data(), tensor->ne[0], tensor->ne[1], ne[0], ne[1]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (split_type == 0) {
|
|
||||||
if (tensor->ne[0]/n_parts != ne[0] || tensor->ne[1] != ne[1]) {
|
|
||||||
fprintf(stderr, "%s: tensor '%s' has wrong shape in model file: got [%d, %d], expected [%d, %d]\n",
|
|
||||||
__func__, name.data(), tensor->ne[0]/n_parts, tensor->ne[1], ne[0], ne[1]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (tensor->ne[0] != ne[0] || tensor->ne[1]/n_parts != ne[1]) {
|
|
||||||
fprintf(stderr, "%s: tensor '%s' has wrong shape in model file: got [%d, %d], expected [%d, %d]\n",
|
|
||||||
__func__, name.data(), tensor->ne[0], tensor->ne[1]/n_parts, ne[0], ne[1]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0) {
|
if (0) {
|
||||||
static const char * ftype_str[] = { "f32", "f16", "q4_0", "q4_1", };
|
static const char * ftype_str[] = { "f32", "f16", "q4_0", "q4_1", };
|
||||||
fprintf(stderr, "%24s - [%5d, %5d], type = %6s, split = %d\n", name.data(), ne[0], ne[1], ftype_str[ftype], split_type);
|
fprintf(stderr, "%24s - [%5d, %5d], type = %6s\n", name.data(), ne[0], ne[1], ftype_str[ftype]);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t bpe = 0;
|
|
||||||
|
|
||||||
switch (ftype) {
|
switch (ftype) {
|
||||||
case 0: bpe = ggml_type_size(GGML_TYPE_F32); break;
|
case 0: // f32
|
||||||
case 1: bpe = ggml_type_size(GGML_TYPE_F16); break;
|
case 1: // f16
|
||||||
case 2: bpe = ggml_type_size(GGML_TYPE_Q4_0); assert(ne[0] % 64 == 0); break;
|
break;
|
||||||
case 3: bpe = ggml_type_size(GGML_TYPE_Q4_1); assert(ne[0] % 64 == 0); break;
|
case 2: // q4_0
|
||||||
|
case 3: // q4_1
|
||||||
|
assert(ne[0] % 64 == 0);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: unknown ftype %d in model file\n", __func__, ftype);
|
fprintf(stderr, "%s: unknown ftype %d in model file\n", __func__, ftype);
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (n_dims == 1 || n_parts == 1) {
|
// load the tensor data into memory without copying or reading it
|
||||||
if ((nelements*bpe)/ggml_blck_size(tensor->type) != ggml_nbytes(tensor)) {
|
size_t offset = fin.tellg();
|
||||||
fprintf(stderr, "%s: tensor '%s' has wrong size in model file: got %zu, expected %zu\n",
|
size_t tensor_data_size = ggml_nbytes(tensor);
|
||||||
__func__, name.data(), ggml_nbytes(tensor), nelements*bpe);
|
offset = (offset + 31) & -32;
|
||||||
return false;
|
tensor->data = mm_addr + offset;
|
||||||
}
|
fin.seekg(offset + tensor_data_size);
|
||||||
|
total_size += tensor_data_size;
|
||||||
if (part_id == 0) {
|
|
||||||
fin.read(reinterpret_cast<char *>(tensor->data), ggml_nbytes(tensor));
|
|
||||||
} else {
|
|
||||||
fin.seekg(ggml_nbytes(tensor), std::ios::cur);
|
|
||||||
}
|
|
||||||
|
|
||||||
total_size += ggml_nbytes(tensor);
|
|
||||||
} else {
|
|
||||||
if ((nelements*bpe)/ggml_blck_size(tensor->type) != ggml_nbytes(tensor)/n_parts) {
|
|
||||||
fprintf(stderr, "%s: tensor '%s' has wrong size in model file: got %zu, expected %zu\n",
|
|
||||||
__func__, name.data(), ggml_nbytes(tensor)/n_parts, nelements*bpe);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (split_type == 0) {
|
|
||||||
const int np0 = ne[0];
|
|
||||||
|
|
||||||
const size_t row_size = (tensor->ne[0]/ggml_blck_size(tensor->type))*ggml_type_size(tensor->type);
|
|
||||||
assert(row_size == tensor->nb[1]);
|
|
||||||
|
|
||||||
for (int i1 = 0; i1 < ne[1]; ++i1) {
|
|
||||||
const size_t offset_row = i1*row_size;
|
|
||||||
const size_t offset = offset_row + ((part_id*np0)/ggml_blck_size(tensor->type))*ggml_type_size(tensor->type);
|
|
||||||
fin.read(reinterpret_cast<char *>(tensor->data) + offset, row_size/n_parts);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const int np1 = ne[1];
|
|
||||||
|
|
||||||
const size_t row_size = (tensor->ne[0]/ggml_blck_size(tensor->type))*ggml_type_size(tensor->type);
|
|
||||||
|
|
||||||
for (int i1 = 0; i1 < ne[1]; ++i1) {
|
|
||||||
const size_t offset_row = (i1 + part_id*np1)*row_size;
|
|
||||||
fin.read(reinterpret_cast<char *>(tensor->data) + offset_row, row_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
total_size += ggml_nbytes(tensor)/n_parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
//fprintf(stderr, "%42s - [%5d, %5d], type = %6s, %6.2f MB\n", name.data(), ne[0], ne[1], ftype == 0 ? "float" : "f16", ggml_nbytes(tensor)/1024.0/1024.0);
|
|
||||||
model.n_loaded++;
|
model.n_loaded++;
|
||||||
|
|
||||||
// progress
|
// progress
|
||||||
if (progress_callback) {
|
if (progress_callback) {
|
||||||
double current_file_progress = double(size_t(fin.tellg()) - file_offset) / double(file_size - file_offset);
|
double current_progress = size_t(fin.tellg()) / double(file_size);
|
||||||
double current_progress = (double(i) + current_file_progress) / double(n_parts);
|
|
||||||
progress_callback(current_progress, progress_callback_user_data);
|
progress_callback(current_progress, progress_callback_user_data);
|
||||||
}
|
}
|
||||||
if (model.n_loaded % 8 == 0) {
|
|
||||||
fprintf(stderr, ".");
|
|
||||||
fflush(stderr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, " done\n");
|
fin.close();
|
||||||
|
|
||||||
fprintf(stderr, "%s: model size = %8.2f MB / num tensors = %d\n", __func__, total_size/1024.0/1024.0, model.n_loaded);
|
fprintf(stderr, "%s: model size = %8.2f MB / num tensors = %d\n", __func__, total_size/1024.0/1024.0, model.n_loaded);
|
||||||
if (model.n_loaded == 0) {
|
if (model.n_loaded == 0) {
|
||||||
@ -800,10 +728,9 @@ static bool llama_model_load(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fin.close();
|
// loading time will be recalculate after the first eval, so
|
||||||
}
|
// we take page faults deferred by mmap() into consideration
|
||||||
|
lctx.t_load_us = ggml_time_us() - lctx.t_start_us;
|
||||||
lctx.t_load_us = ggml_time_us() - t_start_us;
|
|
||||||
|
|
||||||
if (progress_callback) {
|
if (progress_callback) {
|
||||||
progress_callback(1.0, progress_callback_user_data);
|
progress_callback(1.0, progress_callback_user_data);
|
||||||
@ -849,6 +776,7 @@ static bool llama_eval_internal(
|
|||||||
struct ggml_init_params params = {
|
struct ggml_init_params params = {
|
||||||
/*.mem_size =*/ buf_compute.size(),
|
/*.mem_size =*/ buf_compute.size(),
|
||||||
/*.mem_buffer =*/ buf_compute.data(),
|
/*.mem_buffer =*/ buf_compute.data(),
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ggml_context * ctx0 = ggml_init(params);
|
struct ggml_context * ctx0 = ggml_init(params);
|
||||||
@ -856,7 +784,7 @@ static bool llama_eval_internal(
|
|||||||
// for big prompts, if BLAS is enabled, it is better to use only one thread
|
// for big prompts, if BLAS is enabled, it is better to use only one thread
|
||||||
// otherwise, the threads are spin-lock waiting for the BLAS calls and are degrading the performance
|
// otherwise, the threads are spin-lock waiting for the BLAS calls and are degrading the performance
|
||||||
ggml_cgraph gf = {};
|
ggml_cgraph gf = {};
|
||||||
gf.n_threads = N > 255 && ggml_cpu_has_blas() ? 1 : n_threads;
|
gf.n_threads = N >= 32 && ggml_cpu_has_blas() ? 1 : n_threads;
|
||||||
|
|
||||||
struct ggml_tensor * embd = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, N);
|
struct ggml_tensor * embd = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, N);
|
||||||
memcpy(embd->data, tokens, N*ggml_element_size(embd));
|
memcpy(embd->data, tokens, N*ggml_element_size(embd));
|
||||||
@ -922,7 +850,7 @@ static bool llama_eval_internal(
|
|||||||
struct ggml_tensor * KQ_scaled =
|
struct ggml_tensor * KQ_scaled =
|
||||||
ggml_scale(ctx0,
|
ggml_scale(ctx0,
|
||||||
KQ,
|
KQ,
|
||||||
ggml_new_f32(ctx0, 1.0f/sqrt(float(n_embd)/n_head)));
|
ggml_new_f32(ctx0, 1.0f/sqrtf(float(n_embd)/n_head)));
|
||||||
|
|
||||||
// KQ_masked = mask_past(KQ_scaled)
|
// KQ_masked = mask_past(KQ_scaled)
|
||||||
struct ggml_tensor * KQ_masked = ggml_diag_mask_inf(ctx0, KQ_scaled, n_past);
|
struct ggml_tensor * KQ_masked = ggml_diag_mask_inf(ctx0, KQ_scaled, n_past);
|
||||||
@ -1126,7 +1054,7 @@ struct llama_tokenizer {
|
|||||||
size_t offs = 0;
|
size_t offs = 0;
|
||||||
while (offs < text.size()) {
|
while (offs < text.size()) {
|
||||||
llama_sp_symbol sym;
|
llama_sp_symbol sym;
|
||||||
size_t char_len = std::min(text.size() - offs, utf8_len(text[offs]));
|
size_t char_len = Min(text.size() - offs, utf8_len(text[offs]));
|
||||||
sym.text = text.c_str() + offs;
|
sym.text = text.c_str() + offs;
|
||||||
sym.n = char_len;
|
sym.n = char_len;
|
||||||
offs += char_len;
|
offs += char_len;
|
||||||
@ -1240,12 +1168,12 @@ static std::vector<llama_vocab::id> llama_tokenize(const llama_vocab & vocab, co
|
|||||||
// sampling
|
// sampling
|
||||||
//
|
//
|
||||||
|
|
||||||
static void sample_top_k(std::vector<std::pair<double, llama_vocab::id>> & logits_id, int top_k) {
|
static void sample_top_k(std::vector<std::pair<float, llama_vocab::id>> & logits_id, int top_k) {
|
||||||
// find the top k tokens
|
// find the top k tokens
|
||||||
std::partial_sort(
|
std::partial_sort(
|
||||||
logits_id.begin(),
|
logits_id.begin(),
|
||||||
logits_id.begin() + top_k, logits_id.end(),
|
logits_id.begin() + top_k, logits_id.end(),
|
||||||
[](const std::pair<double, llama_vocab::id> & a, const std::pair<double, llama_vocab::id> & b) {
|
[](const std::pair<float, llama_vocab::id> & a, const std::pair<float, llama_vocab::id> & b) {
|
||||||
return a.first > b.first;
|
return a.first > b.first;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1256,9 +1184,9 @@ static llama_vocab::id llama_sample_top_p_top_k(
|
|||||||
llama_context & lctx,
|
llama_context & lctx,
|
||||||
const std::vector<llama_vocab::id> & last_n_tokens,
|
const std::vector<llama_vocab::id> & last_n_tokens,
|
||||||
int top_k,
|
int top_k,
|
||||||
double top_p,
|
float top_p,
|
||||||
double temp,
|
float temp,
|
||||||
double repeat_penalty) {
|
float repeat_penalty) {
|
||||||
auto & rng = lctx.rng;
|
auto & rng = lctx.rng;
|
||||||
|
|
||||||
const int n_logits = lctx.model.hparams.n_vocab;
|
const int n_logits = lctx.model.hparams.n_vocab;
|
||||||
@ -1266,17 +1194,17 @@ static llama_vocab::id llama_sample_top_p_top_k(
|
|||||||
const auto & logits = lctx.logits;
|
const auto & logits = lctx.logits;
|
||||||
const auto * plogits = logits.data() + logits.size() - n_logits;
|
const auto * plogits = logits.data() + logits.size() - n_logits;
|
||||||
|
|
||||||
std::vector<std::pair<double, llama_vocab::id>> logits_id;
|
std::vector<std::pair<float, llama_vocab::id>> logits_id;
|
||||||
logits_id.reserve(n_logits);
|
logits_id.reserve(n_logits);
|
||||||
|
|
||||||
{
|
{
|
||||||
const double scale = 1.0/temp;
|
const float scale = 1.0f/temp;
|
||||||
for (int i = 0; i < n_logits; ++i) {
|
for (int i = 0; i < n_logits; ++i) {
|
||||||
// repetition penalty from ctrl paper (https://arxiv.org/abs/1909.05858)
|
// repetition penalty from ctrl paper (https://arxiv.org/abs/1909.05858)
|
||||||
// credit https://github.com/facebookresearch/llama/compare/main...shawwn:llama:main
|
// credit https://github.com/facebookresearch/llama/compare/main...shawwn:llama:main
|
||||||
if (std::find(last_n_tokens.begin(), last_n_tokens.end(), i) != last_n_tokens.end()) {
|
if (std::find(last_n_tokens.begin(), last_n_tokens.end(), i) != last_n_tokens.end()) {
|
||||||
// if score < 0 then repetition penalty has to multiplied to reduce the previous token probability
|
// if score < 0 then repetition penalty has to multiplied to reduce the previous token probability
|
||||||
if (plogits[i] < 0.0) {
|
if (plogits[i] < 0.0f) {
|
||||||
logits_id.push_back(std::make_pair(plogits[i]*scale*repeat_penalty, i));
|
logits_id.push_back(std::make_pair(plogits[i]*scale*repeat_penalty, i));
|
||||||
} else {
|
} else {
|
||||||
logits_id.push_back(std::make_pair(plogits[i]*scale/repeat_penalty, i));
|
logits_id.push_back(std::make_pair(plogits[i]*scale/repeat_penalty, i));
|
||||||
@ -1289,18 +1217,18 @@ static llama_vocab::id llama_sample_top_p_top_k(
|
|||||||
|
|
||||||
sample_top_k(logits_id, top_k);
|
sample_top_k(logits_id, top_k);
|
||||||
|
|
||||||
double maxl = -std::numeric_limits<double>::infinity();
|
float maxl = -std::numeric_limits<float>::infinity();
|
||||||
for (const auto & kv : logits_id) {
|
for (const auto & kv : logits_id) {
|
||||||
maxl = std::max(maxl, kv.first);
|
maxl = Max(maxl, kv.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute probs for the top k tokens
|
// compute probs for the top k tokens
|
||||||
std::vector<double> probs;
|
std::vector<float> probs;
|
||||||
probs.reserve(logits_id.size());
|
probs.reserve(logits_id.size());
|
||||||
|
|
||||||
double sum = 0.0;
|
double sum = 0.0;
|
||||||
for (const auto & kv : logits_id) {
|
for (const auto & kv : logits_id) {
|
||||||
double p = exp(kv.first - maxl);
|
const float p = expf(kv.first - maxl);
|
||||||
probs.push_back(p);
|
probs.push_back(p);
|
||||||
sum += p;
|
sum += p;
|
||||||
}
|
}
|
||||||
@ -1310,8 +1238,8 @@ static llama_vocab::id llama_sample_top_p_top_k(
|
|||||||
p /= sum;
|
p /= sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top_p < 1.0f) {
|
if (top_p < 1.0) {
|
||||||
double cumsum = 0.0f;
|
double cumsum = 0.0;
|
||||||
for (int i = 0; i < (int) probs.size(); i++) {
|
for (int i = 0; i < (int) probs.size(); i++) {
|
||||||
cumsum += probs[i];
|
cumsum += probs[i];
|
||||||
if (cumsum >= top_p) {
|
if (cumsum >= top_p) {
|
||||||
@ -1345,7 +1273,7 @@ static llama_vocab::id llama_sample_top_p_top_k(
|
|||||||
//
|
//
|
||||||
|
|
||||||
// TODO: reuse code from the llama_model_load() somehow
|
// TODO: reuse code from the llama_model_load() somehow
|
||||||
bool llama_model_quantize_internal(const std::string & fname_inp, const std::string & fname_out, int itype, int qk) {
|
static bool llama_model_quantize_internal(const std::string & fname_inp, const std::string & fname_out, int itype) {
|
||||||
ggml_type type = GGML_TYPE_Q4_1;
|
ggml_type type = GGML_TYPE_Q4_1;
|
||||||
|
|
||||||
switch (itype) {
|
switch (itype) {
|
||||||
@ -1385,8 +1313,7 @@ bool llama_model_quantize_internal(const std::string & fname_inp, const std::str
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (magic != LLAMA_FILE_MAGIC) {
|
if (magic != LLAMA_FILE_MAGIC) {
|
||||||
fprintf(stderr, "%s: invalid model file '%s' (bad magic)\n", __func__, fname_inp.c_str());
|
return report_bad_magic(fname_inp.c_str(), magic, LLAMA_FILE_MAGIC);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fout.write((char *) &magic, sizeof(magic));
|
fout.write((char *) &magic, sizeof(magic));
|
||||||
@ -1444,7 +1371,7 @@ bool llama_model_quantize_internal(const std::string & fname_inp, const std::str
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string word;
|
std::vector<char> word(32);
|
||||||
vocab.id_to_token.resize(n_vocab);
|
vocab.id_to_token.resize(n_vocab);
|
||||||
for (int i = 0; i < n_vocab; i++) {
|
for (int i = 0; i < n_vocab; i++) {
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
@ -1452,17 +1379,17 @@ bool llama_model_quantize_internal(const std::string & fname_inp, const std::str
|
|||||||
fout.write((char *) &len, sizeof(len));
|
fout.write((char *) &len, sizeof(len));
|
||||||
|
|
||||||
word.resize(len);
|
word.resize(len);
|
||||||
finp.read ((char *) word.data(), len);
|
finp.read ((char *) &word[0], len);
|
||||||
fout.write((char *) word.data(), len);
|
fout.write((char *) &word[0], len);
|
||||||
|
|
||||||
float score;
|
float score;
|
||||||
finp.read ((char *) &score, sizeof(score));
|
finp.read ((char *) &score, sizeof(score));
|
||||||
fout.write((char *) &score, sizeof(score));
|
fout.write((char *) &score, sizeof(score));
|
||||||
|
|
||||||
vocab.token_to_id[word] = i;
|
vocab.token_to_id[word.data()] = i;
|
||||||
|
|
||||||
auto &tok_score = vocab.id_to_token[i];
|
auto &tok_score = vocab.id_to_token[i];
|
||||||
tok_score.tok = word;
|
tok_score.tok = word.data();
|
||||||
tok_score.score = score;
|
tok_score.score = score;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1503,6 +1430,13 @@ bool llama_model_quantize_internal(const std::string & fname_inp, const std::str
|
|||||||
std::string name(length, 0);
|
std::string name(length, 0);
|
||||||
finp.read (&name[0], length);
|
finp.read (&name[0], length);
|
||||||
|
|
||||||
|
{
|
||||||
|
// ensure tensor data is aligned
|
||||||
|
uint64_t offset = finp.tellg();
|
||||||
|
offset = (offset + 31) & -32;
|
||||||
|
finp.seekg(offset);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
static const char * ftype_str[] = { "f32", "f16", "q4_0", "q4_1", };
|
static const char * ftype_str[] = { "f32", "f16", "q4_0", "q4_1", };
|
||||||
printf("%48s - [%5d, %5d], type = %6s ", name.data(), ne[0], ne[1], ftype_str[ftype]);
|
printf("%48s - [%5d, %5d], type = %6s ", name.data(), ne[0], ne[1], ftype_str[ftype]);
|
||||||
@ -1558,6 +1492,13 @@ bool llama_model_quantize_internal(const std::string & fname_inp, const std::str
|
|||||||
}
|
}
|
||||||
fout.write(&name[0], length);
|
fout.write(&name[0], length);
|
||||||
|
|
||||||
|
{
|
||||||
|
// ensure tensor data is aligned
|
||||||
|
uint64_t offset = fout.tellp();
|
||||||
|
offset = (offset + 31) & -32;
|
||||||
|
fout.seekp(offset);
|
||||||
|
}
|
||||||
|
|
||||||
if (quantize) {
|
if (quantize) {
|
||||||
printf("quantizing .. ");
|
printf("quantizing .. ");
|
||||||
work.resize(nelements); // for quantization
|
work.resize(nelements); // for quantization
|
||||||
@ -1568,11 +1509,11 @@ bool llama_model_quantize_internal(const std::string & fname_inp, const std::str
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case GGML_TYPE_Q4_0:
|
case GGML_TYPE_Q4_0:
|
||||||
{
|
{
|
||||||
cur_size = ggml_quantize_q4_0(data_f32.data(), work.data(), nelements, ne[0], qk, hist_cur.data());
|
cur_size = ggml_quantize_q4_0(data_f32.data(), work.data(), nelements, ne[0], hist_cur.data());
|
||||||
} break;
|
} break;
|
||||||
case GGML_TYPE_Q4_1:
|
case GGML_TYPE_Q4_1:
|
||||||
{
|
{
|
||||||
cur_size = ggml_quantize_q4_1(data_f32.data(), work.data(), nelements, ne[0], qk, hist_cur.data());
|
cur_size = ggml_quantize_q4_1(data_f32.data(), work.data(), nelements, ne[0], hist_cur.data());
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
@ -1590,7 +1531,7 @@ bool llama_model_quantize_internal(const std::string & fname_inp, const std::str
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < (int) hist_cur.size(); ++i) {
|
for (int i = 0; i < (int) hist_cur.size(); ++i) {
|
||||||
printf("%5.3f ", hist_cur[i] / (float)nelements);
|
printf("%5.3f ", hist_cur[i] / float(nelements));
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
} else {
|
} else {
|
||||||
@ -1613,7 +1554,7 @@ bool llama_model_quantize_internal(const std::string & fname_inp, const std::str
|
|||||||
|
|
||||||
printf("%s: hist: ", __func__);
|
printf("%s: hist: ", __func__);
|
||||||
for (int i = 0; i < (int) hist_all.size(); ++i) {
|
for (int i = 0; i < (int) hist_all.size(); ++i) {
|
||||||
printf("%5.3f ", hist_all[i] / (float)sum_all);
|
printf("%5.3f ", hist_all[i] / float(sum_all));
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
@ -1655,7 +1596,10 @@ struct llama_context * llama_init_from_file(
|
|||||||
|
|
||||||
if (params.use_mlock) {
|
if (params.use_mlock) {
|
||||||
char *err;
|
char *err;
|
||||||
if (!ggml_mlock(ctx->model.ctx, &err)) {
|
if (!ggml_mlock(ctx->model.ctx,
|
||||||
|
ctx->model.mm_addr,
|
||||||
|
ctx->model.mm_length,
|
||||||
|
&err)) {
|
||||||
fprintf(stderr, "%s\n", err);
|
fprintf(stderr, "%s\n", err);
|
||||||
free(err);
|
free(err);
|
||||||
llama_free(ctx);
|
llama_free(ctx);
|
||||||
@ -1705,15 +1649,18 @@ void llama_free(struct llama_context * ctx) {
|
|||||||
ggml_free(ctx->model.ctx);
|
ggml_free(ctx->model.ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx->model.mm_addr) {
|
||||||
|
munmap_file(ctx->model.mm_addr, ctx->model.mm_length);
|
||||||
|
}
|
||||||
|
|
||||||
delete ctx;
|
delete ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
int llama_model_quantize(
|
int llama_model_quantize(
|
||||||
const char * fname_inp,
|
const char * fname_inp,
|
||||||
const char * fname_out,
|
const char * fname_out,
|
||||||
int itype,
|
int itype) {
|
||||||
int qk) {
|
if (!llama_model_quantize_internal(fname_inp, fname_out, itype)) {
|
||||||
if (!llama_model_quantize_internal(fname_inp, fname_out, itype, qk)) {
|
|
||||||
fprintf(stderr, "%s: failed to quantize\n", __func__);
|
fprintf(stderr, "%s: failed to quantize\n", __func__);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1731,7 +1678,11 @@ int llama_eval(
|
|||||||
fprintf(stderr, "%s: failed to eval\n", __func__);
|
fprintf(stderr, "%s: failed to eval\n", __func__);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
// get a more accurate load time, upon first eval
|
||||||
|
if (!ctx->has_evaluated_once) {
|
||||||
|
ctx->t_load_us = ggml_time_us() - ctx->t_start_us;
|
||||||
|
ctx->has_evaluated_once = true;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1796,9 +1747,9 @@ llama_token llama_sample_top_p_top_k(
|
|||||||
const llama_token * last_n_tokens_data,
|
const llama_token * last_n_tokens_data,
|
||||||
int last_n_tokens_size,
|
int last_n_tokens_size,
|
||||||
int top_k,
|
int top_k,
|
||||||
double top_p,
|
float top_p,
|
||||||
double temp,
|
float temp,
|
||||||
double repeat_penalty) {
|
float repeat_penalty) {
|
||||||
const int64_t t_start_sample_us = ggml_time_us();
|
const int64_t t_start_sample_us = ggml_time_us();
|
||||||
|
|
||||||
llama_token result = 0;
|
llama_token result = 0;
|
||||||
@ -1824,21 +1775,20 @@ llama_token llama_sample_top_p_top_k(
|
|||||||
void llama_print_timings(struct llama_context * ctx) {
|
void llama_print_timings(struct llama_context * ctx) {
|
||||||
const int64_t t_end_us = ggml_time_us();
|
const int64_t t_end_us = ggml_time_us();
|
||||||
|
|
||||||
const int32_t n_sample = std::max(1, ctx->n_sample);
|
const int32_t n_sample = Max(1, ctx->n_sample);
|
||||||
const int32_t n_eval = std::max(1, ctx->n_eval);
|
const int32_t n_eval = Max(1, ctx->n_eval);
|
||||||
const int32_t n_p_eval = std::max(1, ctx->n_p_eval);
|
const int32_t n_p_eval = Max(1, ctx->n_p_eval);
|
||||||
|
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
fprintf(stderr, "%s: load time = %8.2f ms\n", __func__, ctx->t_load_us / 1000.0f);
|
fprintf(stderr, "%s: load time = %8.2f ms\n", __func__, ctx->t_load_us / 1000.0);
|
||||||
fprintf(stderr, "%s: sample time = %8.2f ms / %5d runs (%8.2f ms per run)\n", __func__, 1e-3f * ctx->t_sample_us, n_sample, 1e-3f * ctx->t_sample_us / n_sample);
|
fprintf(stderr, "%s: sample time = %8.2f ms / %5d runs (%8.2f ms per run)\n", __func__, 1e-3 * ctx->t_sample_us, n_sample, 1e-3 * ctx->t_sample_us / n_sample);
|
||||||
fprintf(stderr, "%s: prompt eval time = %8.2f ms / %5d tokens (%8.2f ms per token)\n", __func__, 1e-3f * ctx->t_p_eval_us, n_p_eval, 1e-3f * ctx->t_p_eval_us / n_p_eval);
|
fprintf(stderr, "%s: prompt eval time = %8.2f ms / %5d tokens (%8.2f ms per token)\n", __func__, 1e-3 * ctx->t_p_eval_us, n_p_eval, 1e-3 * ctx->t_p_eval_us / n_p_eval);
|
||||||
fprintf(stderr, "%s: eval time = %8.2f ms / %5d runs (%8.2f ms per run)\n", __func__, 1e-3f * ctx->t_eval_us, n_eval, 1e-3f * ctx->t_eval_us / n_eval);
|
fprintf(stderr, "%s: eval time = %8.2f ms / %5d runs (%8.2f ms per run)\n", __func__, 1e-3 * ctx->t_eval_us, n_eval, 1e-3 * ctx->t_eval_us / n_eval);
|
||||||
fprintf(stderr, "%s: total time = %8.2f ms\n", __func__, (t_end_us - ctx->t_start_us)/1000.0f);
|
fprintf(stderr, "%s: total time = %8.2f ms\n", __func__, (t_end_us - ctx->t_start_us)/1000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void llama_reset_timings(struct llama_context * ctx) {
|
void llama_reset_timings(struct llama_context * ctx) {
|
||||||
ctx->t_start_us = ggml_time_us();
|
ctx->t_start_us = ggml_time_us();
|
||||||
|
|
||||||
ctx->t_sample_us = ctx->n_sample = 0;
|
ctx->t_sample_us = ctx->n_sample = 0;
|
||||||
ctx->t_eval_us = ctx->n_eval = 0;
|
ctx->t_eval_us = ctx->n_eval = 0;
|
||||||
ctx->t_p_eval_us = ctx->n_p_eval = 0;
|
ctx->t_p_eval_us = ctx->n_p_eval = 0;
|
@ -6,7 +6,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef LLAMA_SHARED
|
#ifdef LLAMA_SHARED
|
||||||
# ifdef _WIN32
|
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
# ifdef LLAMA_BUILD
|
# ifdef LLAMA_BUILD
|
||||||
# define LLAMA_API __declspec(dllexport)
|
# define LLAMA_API __declspec(dllexport)
|
||||||
# else
|
# else
|
||||||
@ -20,7 +20,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LLAMA_FILE_VERSION 1
|
#define LLAMA_FILE_VERSION 1
|
||||||
#define LLAMA_FILE_MAGIC 0x67676d66 // 'ggmf' in hex
|
#define LLAMA_FILE_MAGIC 0x67676a74 // 'ggjt' in hex
|
||||||
#define LLAMA_FILE_MAGIC_UNVERSIONED 0x67676d6c // pre-versioned files
|
#define LLAMA_FILE_MAGIC_UNVERSIONED 0x67676d6c // pre-versioned files
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -45,7 +45,7 @@ extern "C" {
|
|||||||
|
|
||||||
} llama_token_data;
|
} llama_token_data;
|
||||||
|
|
||||||
typedef void (*llama_progress_callback)(double progress, void *ctx);
|
typedef void (*llama_progress_callback)(float progress, void *ctx);
|
||||||
|
|
||||||
struct llama_context_params {
|
struct llama_context_params {
|
||||||
int n_ctx; // text context
|
int n_ctx; // text context
|
||||||
@ -81,8 +81,7 @@ extern "C" {
|
|||||||
LLAMA_API int llama_model_quantize(
|
LLAMA_API int llama_model_quantize(
|
||||||
const char * fname_inp,
|
const char * fname_inp,
|
||||||
const char * fname_out,
|
const char * fname_out,
|
||||||
int itype,
|
int itype);
|
||||||
int qk);
|
|
||||||
|
|
||||||
// Run the llama inference to obtain the logits and probabilities for the next token.
|
// Run the llama inference to obtain the logits and probabilities for the next token.
|
||||||
// tokens + n_tokens is the provided batch of new tokens to process
|
// tokens + n_tokens is the provided batch of new tokens to process
|
||||||
@ -135,9 +134,9 @@ extern "C" {
|
|||||||
const llama_token * last_n_tokens_data,
|
const llama_token * last_n_tokens_data,
|
||||||
int last_n_tokens_size,
|
int last_n_tokens_size,
|
||||||
int top_k,
|
int top_k,
|
||||||
double top_p,
|
float top_p,
|
||||||
double temp,
|
float temp,
|
||||||
double repeat_penalty);
|
float repeat_penalty);
|
||||||
|
|
||||||
// Performance information
|
// Performance information
|
||||||
LLAMA_API void llama_print_timings(struct llama_context * ctx);
|
LLAMA_API void llama_print_timings(struct llama_context * ctx);
|
23
examples/talk-llama/prompts/talk-alpaca.txt
Normal file
23
examples/talk-llama/prompts/talk-alpaca.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Below is an instruction that describes a task. Write a response that appropriately completes the request.
|
||||||
|
|
||||||
|
### Instruction:
|
||||||
|
|
||||||
|
Write a text transcript of a never ending dialog, where {0} interacts with an AI assistant named {1}.
|
||||||
|
{1} is helpful, kind, honest, friendly, good at writing and never fails to answer {0}’s requests immediately and with details and precision.
|
||||||
|
There are no annotations like (30 seconds passed...) or (to himself), just what {0} and {1} say aloud to each other.
|
||||||
|
The transcript only includes text, it does not include markup like HTML and Markdown.
|
||||||
|
{1} responds with short and concise answers.
|
||||||
|
|
||||||
|
### Response:
|
||||||
|
|
||||||
|
{0}{4} Hello, {1}!
|
||||||
|
{1}{4} Hello {0}! How may I help you today?
|
||||||
|
{0}{4} What time is it?
|
||||||
|
{1}{4} It is {2} o'clock.
|
||||||
|
{0}{4} What year is it?
|
||||||
|
{1}{4} We are in {3}.
|
||||||
|
{0}{4} What is a cat?
|
||||||
|
{1}{4} A cat is a domestic species of small carnivorous mammal. It is the only domesticated species in the family Felidae.
|
||||||
|
{0}{4} Name a color.
|
||||||
|
{1}{4} Blue
|
||||||
|
{0}{4}
|
@ -10,7 +10,15 @@
|
|||||||
#espeak -v en-us+m$1 -s 225 -p 50 -a 200 -g 5 -k 5 "$2"
|
#espeak -v en-us+m$1 -s 225 -p 50 -a 200 -g 5 -k 5 "$2"
|
||||||
|
|
||||||
# for Mac
|
# for Mac
|
||||||
say "$2"
|
if [ "$1" = "0" ]; then
|
||||||
|
say "$2"
|
||||||
|
elif [ "$1" = "1" ]; then
|
||||||
|
say -v "Samantha (Enhanced)" "$2"
|
||||||
|
elif [ "$1" = "2" ]; then
|
||||||
|
say -v "Daniel (Enhanced)" "$2"
|
||||||
|
elif [ "$1" = "3" ]; then
|
||||||
|
say -v "Veena (Enhanced)" "$2"
|
||||||
|
fi
|
||||||
|
|
||||||
# Eleven Labs
|
# Eleven Labs
|
||||||
#
|
#
|
703
examples/talk-llama/talk-llama.cpp
Normal file
703
examples/talk-llama/talk-llama.cpp
Normal file
@ -0,0 +1,703 @@
|
|||||||
|
// Talk with AI
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "common-sdl.h"
|
||||||
|
#include "whisper.h"
|
||||||
|
#include "llama.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <fstream>
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
std::vector<llama_token> llama_tokenize(struct llama_context * ctx, const std::string & text, bool add_bos) {
|
||||||
|
// initialize to prompt numer of chars, since n_tokens <= n_prompt_chars
|
||||||
|
std::vector<llama_token> res(text.size() + (int)add_bos);
|
||||||
|
int n = llama_tokenize(ctx, text.c_str(), res.data(), res.size(), add_bos);
|
||||||
|
assert(n >= 0);
|
||||||
|
res.resize(n);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// command-line parameters
|
||||||
|
struct whisper_params {
|
||||||
|
int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
|
||||||
|
int32_t voice_id = 0;
|
||||||
|
int32_t voice_ms = 10000;
|
||||||
|
int32_t capture_id = -1;
|
||||||
|
int32_t max_tokens = 64;
|
||||||
|
int32_t audio_ctx = 0;
|
||||||
|
|
||||||
|
int32_t n_parts_llama = -1;
|
||||||
|
|
||||||
|
float vad_thold = 0.4f;
|
||||||
|
float freq_thold = 100.0f;
|
||||||
|
|
||||||
|
bool speed_up = false;
|
||||||
|
bool translate = false;
|
||||||
|
bool print_special = false;
|
||||||
|
bool print_energy = false;
|
||||||
|
bool no_timestamps = true;
|
||||||
|
bool verbose_prompt = false;
|
||||||
|
|
||||||
|
std::string name_ni = "Georgi"; // natural intelligence
|
||||||
|
std::string name_ai = "LLaMA"; // artificial intelligence
|
||||||
|
std::string language = "en";
|
||||||
|
std::string model_wsp = "models/ggml-base.en.bin";
|
||||||
|
std::string model_llama = "models/ggml-llama-7B.bin";
|
||||||
|
std::string speak = "./examples/talk/speak.sh";
|
||||||
|
std::string prompt = "";
|
||||||
|
std::string fname_out;
|
||||||
|
};
|
||||||
|
|
||||||
|
void whisper_print_usage(int argc, char ** argv, const whisper_params & params);
|
||||||
|
|
||||||
|
bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
std::string arg = argv[i];
|
||||||
|
|
||||||
|
if (arg == "-h" || arg == "--help") {
|
||||||
|
whisper_print_usage(argc, argv, params);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
else if (arg == "-t" || arg == "--threads") { params.n_threads = std::stoi(argv[++i]); }
|
||||||
|
else if (arg == "-vid" || arg == "--voice-id") { params.voice_id = std::stoi(argv[++i]); }
|
||||||
|
else if (arg == "-vms" || arg == "--voice-ms") { params.voice_ms = std::stoi(argv[++i]); }
|
||||||
|
else if (arg == "-c" || arg == "--capture") { params.capture_id = std::stoi(argv[++i]); }
|
||||||
|
else if (arg == "-mt" || arg == "--max-tokens") { params.max_tokens = std::stoi(argv[++i]); }
|
||||||
|
else if (arg == "-ac" || arg == "--audio-ctx") { params.audio_ctx = std::stoi(argv[++i]); }
|
||||||
|
else if (arg == "-vth" || arg == "--vad-thold") { params.vad_thold = std::stof(argv[++i]); }
|
||||||
|
else if (arg == "-fth" || arg == "--freq-thold") { params.freq_thold = std::stof(argv[++i]); }
|
||||||
|
else if (arg == "--n-parts-llama") { params.n_parts_llama = std::stoi(argv[++i]); }
|
||||||
|
else if (arg == "-su" || arg == "--speed-up") { params.speed_up = true; }
|
||||||
|
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
|
||||||
|
else if (arg == "-ps" || arg == "--print-special") { params.print_special = true; }
|
||||||
|
else if (arg == "-pe" || arg == "--print-energy") { params.print_energy = true; }
|
||||||
|
else if (arg == "--verbose-prompt") { params.verbose_prompt = true; }
|
||||||
|
else if (arg == "-nni" || arg == "--name-ni") { params.name_ni = argv[++i]; }
|
||||||
|
else if (arg == "-nai" || arg == "--name-ai") { params.name_ai = argv[++i]; }
|
||||||
|
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
|
||||||
|
else if (arg == "-mw" || arg == "--model-whisper") { params.model_wsp = argv[++i]; }
|
||||||
|
else if (arg == "-ml" || arg == "--model-llama") { params.model_llama = argv[++i]; }
|
||||||
|
else if (arg == "-s" || arg == "--speak") { params.speak = argv[++i]; }
|
||||||
|
else if (arg == "--prompt-file") {
|
||||||
|
std::ifstream file(argv[++i]);
|
||||||
|
std::copy(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>(), back_inserter(params.prompt));
|
||||||
|
if (params.prompt.back() == '\n') {
|
||||||
|
params.prompt.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (arg == "-f" || arg == "--file") { params.fname_out = argv[++i]; }
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
||||||
|
whisper_print_usage(argc, argv, params);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & params) {
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
fprintf(stderr, "usage: %s [options]\n", argv[0]);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
fprintf(stderr, "options:\n");
|
||||||
|
fprintf(stderr, " -h, --help [default] show this help message and exit\n");
|
||||||
|
fprintf(stderr, " -t N, --threads N [%-7d] number of threads to use during computation\n", params.n_threads);
|
||||||
|
fprintf(stderr, " -vid N, --voice-id N [%-7d] voice ID\n", params.voice_id);
|
||||||
|
fprintf(stderr, " -vms N, --voice-ms N [%-7d] voice duration in milliseconds\n", params.voice_ms);
|
||||||
|
fprintf(stderr, " -c ID, --capture ID [%-7d] capture device ID\n", params.capture_id);
|
||||||
|
fprintf(stderr, " -mt N, --max-tokens N [%-7d] maximum number of tokens per audio chunk\n", params.max_tokens);
|
||||||
|
fprintf(stderr, " -ac N, --audio-ctx N [%-7d] audio context size (0 - all)\n", params.audio_ctx);
|
||||||
|
fprintf(stderr, " -vth N, --vad-thold N [%-7.2f] voice activity detection threshold\n", params.vad_thold);
|
||||||
|
fprintf(stderr, " -fth N, --freq-thold N [%-7.2f] high-pass frequency cutoff\n", params.freq_thold);
|
||||||
|
fprintf(stderr, " -su, --speed-up [%-7s] speed up audio by x2 (reduced accuracy)\n", params.speed_up ? "true" : "false");
|
||||||
|
fprintf(stderr, " -tr, --translate [%-7s] translate from source language to english\n", params.translate ? "true" : "false");
|
||||||
|
fprintf(stderr, " -ps, --print-special [%-7s] print special tokens\n", params.print_special ? "true" : "false");
|
||||||
|
fprintf(stderr, " -pe, --print-energy [%-7s] print sound energy (for debugging)\n", params.print_energy ? "true" : "false");
|
||||||
|
fprintf(stderr, " -nni NAME,--name-ni NAME [%-7s] natural intelligence name\n", params.name_ni.c_str());
|
||||||
|
fprintf(stderr, " -nai NAME,--name-ai NAME [%-7s] artificial intelligence name\n", params.name_ai.c_str());
|
||||||
|
fprintf(stderr, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str());
|
||||||
|
fprintf(stderr, " -mw FILE, --model-whisper [%-7s] whisper model file\n", params.model_wsp.c_str());
|
||||||
|
fprintf(stderr, " -ml FILE, --model-llama [%-7s] llama model file\n", params.model_llama.c_str());
|
||||||
|
fprintf(stderr, " --n-parts-llama N [%-7d] num parts in llama model file\n", params.n_parts_llama);
|
||||||
|
fprintf(stderr, " -s FILE, --speak TEXT [%-7s] command for TTS\n", params.speak.c_str());
|
||||||
|
fprintf(stderr, " --prompt-file FNAME [%-7s] file with custom prompt to start dialog\n", "");
|
||||||
|
fprintf(stderr, " --verbose-prompt [%-7s] print prompt at start\n", params.verbose_prompt ? "true" : "false");
|
||||||
|
fprintf(stderr, " -f FNAME, --file FNAME [%-7s] text output file name\n", params.fname_out.c_str());
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string transcribe(
|
||||||
|
whisper_context * ctx,
|
||||||
|
const whisper_params & params,
|
||||||
|
const std::vector<float> & pcmf32,
|
||||||
|
const std::string prompt_text,
|
||||||
|
float & prob,
|
||||||
|
int64_t & t_ms) {
|
||||||
|
const auto t_start = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
prob = 0.0f;
|
||||||
|
t_ms = 0;
|
||||||
|
|
||||||
|
std::vector<whisper_token> prompt_tokens;
|
||||||
|
|
||||||
|
whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY);
|
||||||
|
|
||||||
|
prompt_tokens.resize(1024);
|
||||||
|
prompt_tokens.resize(whisper_tokenize(ctx, prompt_text.c_str(), prompt_tokens.data(), prompt_tokens.size()));
|
||||||
|
|
||||||
|
wparams.print_progress = false;
|
||||||
|
wparams.print_special = params.print_special;
|
||||||
|
wparams.print_realtime = false;
|
||||||
|
wparams.print_timestamps = !params.no_timestamps;
|
||||||
|
wparams.translate = params.translate;
|
||||||
|
wparams.no_context = true;
|
||||||
|
wparams.single_segment = true;
|
||||||
|
wparams.max_tokens = params.max_tokens;
|
||||||
|
wparams.language = params.language.c_str();
|
||||||
|
wparams.n_threads = 2;
|
||||||
|
|
||||||
|
wparams.prompt_tokens = prompt_tokens.empty() ? nullptr : prompt_tokens.data();
|
||||||
|
wparams.prompt_n_tokens = prompt_tokens.empty() ? 0 : prompt_tokens.size();
|
||||||
|
|
||||||
|
wparams.audio_ctx = params.audio_ctx;
|
||||||
|
wparams.speed_up = params.speed_up;
|
||||||
|
|
||||||
|
static int iter = params.voice_id;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100*iter));
|
||||||
|
iter = (iter + 1) % 4;
|
||||||
|
|
||||||
|
if (whisper_full(ctx, wparams, pcmf32.data(), pcmf32.size()) != 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int prob_n = 0;
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
const int n_segments = whisper_full_n_segments(ctx);
|
||||||
|
for (int i = 0; i < n_segments; ++i) {
|
||||||
|
const char * text = whisper_full_get_segment_text(ctx, i);
|
||||||
|
|
||||||
|
result += text;
|
||||||
|
|
||||||
|
const int n_tokens = whisper_full_n_tokens(ctx, i);
|
||||||
|
for (int j = 0; j < n_tokens; ++j) {
|
||||||
|
const auto token = whisper_full_get_token_data(ctx, i, j);
|
||||||
|
|
||||||
|
prob += token.p;
|
||||||
|
++prob_n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prob_n > 0) {
|
||||||
|
prob /= prob_n;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto t_end = std::chrono::high_resolution_clock::now();
|
||||||
|
t_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t_end - t_start).count();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> k_participants = {
|
||||||
|
"LLaMA",
|
||||||
|
"GGaMA",
|
||||||
|
"SSaMA",
|
||||||
|
"RRaMA",
|
||||||
|
};
|
||||||
|
|
||||||
|
// homophones
|
||||||
|
const std::map<std::string, std::vector<std::string>> k_homophones = {
|
||||||
|
{ "LLaMA", { "llama", "Llama", "LLAMA", }, },
|
||||||
|
{ "GGaMA", { "gama", "Gama", "GAMA", "gamma", "Gamma", "GAMMA", }, },
|
||||||
|
{ "SSaMA", { "sama", "Sama", "SAMA", "samma", "Samma", "SAMMA", }, },
|
||||||
|
{ "RRaMA", { "rama", "Rama", "RAMA", "ramma", "Ramma", "RAMMA", }, },
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string k_prompt_whisper = R"(A conversation between {1}, {10}, {11}, {12} and {13}.)";
|
||||||
|
|
||||||
|
const std::map<std::string, std::string> k_prompt = {
|
||||||
|
{
|
||||||
|
k_participants.at(0),
|
||||||
|
R"(Text transcript of a never ending dialog, between {1}, {10}, {11}, {12} and {13}.
|
||||||
|
There are no annotations like (30 seconds passed...) or (to himself), just what the participants say aloud to each other.
|
||||||
|
The transcript only includes text, it does not include markup like HTML and Markdown.
|
||||||
|
{10}, {11}, {12} and {13} respond with short and concise answers.
|
||||||
|
{10} is smart, objective, honest and kind. Never fails to give a meaningful and insightful answer and opinion.
|
||||||
|
{1} is leading the conversation and asking the questions.
|
||||||
|
|
||||||
|
{1}{4} Hello {10}! What is your opinion on the current state of the world?
|
||||||
|
{10}{4} Great question {1}! I think we live in a very interesting time.
|
||||||
|
There are many things to be concerned about, but also many things to be optimistic about.
|
||||||
|
{1}{4} What advice would you give to a young person who is just starting out in life?
|
||||||
|
{10}{4} I would tell them to be patient and to not be afraid to fail.
|
||||||
|
It is important to learn from your mistakes and to keep trying.
|
||||||
|
{1}{4})"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k_participants.at(1),
|
||||||
|
R"(Text transcript of a never ending dialog, between {1}, {10}, {11}, {12} and {13}.
|
||||||
|
There are no annotations like (30 seconds passed...) or (to himself), just what the participants say aloud to each other.
|
||||||
|
The transcript only includes text, it does not include markup like HTML and Markdown.
|
||||||
|
{10}, {11}, {12} and {13} respond with short and concise answers.
|
||||||
|
{11} has critical thinking skills, is very knowledgeable and is a good listener. He is very humble and never arrogant.
|
||||||
|
{1} is leading the conversation and asking the questions.
|
||||||
|
|
||||||
|
{1}{4} Hello {11}! What is your opinion on the current state of the world?
|
||||||
|
{11}{4} The world is about to experience a major change. We are on the verge of a new era.
|
||||||
|
{1}{4} What advice would you give to a young person who is just starting out in life?
|
||||||
|
{11}{4} My advice would be to be open minded and to be willing to learn from others.
|
||||||
|
{1}{4})"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k_participants.at(2),
|
||||||
|
R"(Text transcript of a never ending dialog, between {1}, {10}, {11}, {12} and {13}.
|
||||||
|
There are no annotations like (30 seconds passed...) or (to himself), just what the participants say aloud to each other.
|
||||||
|
The transcript only includes text, it does not include markup like HTML and Markdown.
|
||||||
|
{10}, {11}, {12} and {13} respond with short and concise answers.
|
||||||
|
{12} has strong leadership skills, strategic thinking, and innovative ideas. Has the ability to mentor and support young people.
|
||||||
|
{1} is leading the conversation and asking the questions.
|
||||||
|
|
||||||
|
{1}{4} Hello {12}! What is your opinion on the current state of the world?
|
||||||
|
{12}{4} Our future is bright. We are living in a time of great opportunity.
|
||||||
|
{1}{4} What advice would you give to a young person who is just starting out in life?
|
||||||
|
{12}{4} I would tell them to be brave and to be willing to take risks.
|
||||||
|
{1}{4})"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
k_participants.at(3),
|
||||||
|
R"(Text transcript of a never ending dialog, between {1}, {10}, {11}, {12} and {13}.
|
||||||
|
There are no annotations like (30 seconds passed...) or (to himself), just what the participants say aloud to each other.
|
||||||
|
The transcript only includes text, it does not include markup like HTML and Markdown.
|
||||||
|
{10}, {11}, {12} and {13} respond with short and concise answers.
|
||||||
|
{13} is rude, arrogant, and has a bad attitude. He is very opinionated and never listens to others.
|
||||||
|
{1} is leading the conversation and asking the questions.
|
||||||
|
|
||||||
|
{1}{4} Hello {13}! What is your opinion on the current state of the world?
|
||||||
|
{13}{4} The world is a terrible place. It is full of evil and corruption.
|
||||||
|
{1}{4} What advice would you give to a young person who is just starting out in life?
|
||||||
|
{13}{4} I would tell them to be selfish and to never trust anyone.
|
||||||
|
{1}{4})"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char ** argv) {
|
||||||
|
whisper_params params;
|
||||||
|
|
||||||
|
if (whisper_params_parse(argc, argv, params) == false) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (whisper_lang_id(params.language.c_str()) == -1) {
|
||||||
|
fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str());
|
||||||
|
whisper_print_usage(argc, argv, params);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// whisper init
|
||||||
|
|
||||||
|
struct whisper_context * ctx_wsp = whisper_init_from_file(params.model_wsp.c_str());
|
||||||
|
|
||||||
|
// llama init
|
||||||
|
|
||||||
|
auto lparams = llama_context_default_params();
|
||||||
|
|
||||||
|
// tune these to your liking
|
||||||
|
lparams.n_ctx = 512;
|
||||||
|
lparams.seed = 1;
|
||||||
|
lparams.f16_kv = true;
|
||||||
|
lparams.n_parts = params.n_parts_llama;
|
||||||
|
|
||||||
|
struct llama_context * ctx_llama = llama_init_from_file(params.model_llama.c_str(), lparams);
|
||||||
|
|
||||||
|
// print some info about the processing
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
if (!whisper_is_multilingual(ctx_wsp)) {
|
||||||
|
if (params.language != "en" || params.translate) {
|
||||||
|
params.language = "en";
|
||||||
|
params.translate = false;
|
||||||
|
fprintf(stderr, "%s: WARNING: model is not multilingual, ignoring language and translation options\n", __func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%s: processing, %d threads, lang = %s, task = %s, timestamps = %d ...\n",
|
||||||
|
__func__,
|
||||||
|
params.n_threads,
|
||||||
|
params.language.c_str(),
|
||||||
|
params.translate ? "translate" : "transcribe",
|
||||||
|
params.no_timestamps ? 0 : 1);
|
||||||
|
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// init audio
|
||||||
|
|
||||||
|
audio_async audio(30*1000);
|
||||||
|
if (!audio.init(params.capture_id, WHISPER_SAMPLE_RATE)) {
|
||||||
|
fprintf(stderr, "%s: audio.init() failed!\n", __func__);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio.resume();
|
||||||
|
|
||||||
|
int n_iter = 0;
|
||||||
|
|
||||||
|
bool is_running = true;
|
||||||
|
bool force_speak = false;
|
||||||
|
|
||||||
|
float prob0 = 0.0f;
|
||||||
|
|
||||||
|
const std::string chat_symb = ":";
|
||||||
|
|
||||||
|
const std::string name_ni = params.name_ni;
|
||||||
|
const std::string name_ai = params.name_ai;
|
||||||
|
|
||||||
|
// the participant that was referenced last
|
||||||
|
std::string name_ref = name_ni;
|
||||||
|
|
||||||
|
std::vector<float> pcmf32_cur;
|
||||||
|
std::vector<float> pcmf32_prompt;
|
||||||
|
|
||||||
|
std::string prompt_whisper = k_prompt_whisper;
|
||||||
|
|
||||||
|
prompt_whisper = ::replace(prompt_whisper, "{1}", name_ni);
|
||||||
|
prompt_whisper = ::replace(prompt_whisper, "{10}", k_participants.at(0));
|
||||||
|
prompt_whisper = ::replace(prompt_whisper, "{11}", k_participants.at(1));
|
||||||
|
prompt_whisper = ::replace(prompt_whisper, "{12}", k_participants.at(2));
|
||||||
|
prompt_whisper = ::replace(prompt_whisper, "{13}", k_participants.at(3));
|
||||||
|
|
||||||
|
// construct the initial prompt for LLaMA inference
|
||||||
|
std::string prompt_llama = params.prompt.empty() ? k_prompt.find(name_ai)->second : params.prompt;
|
||||||
|
|
||||||
|
// need to have leading ' '
|
||||||
|
prompt_llama.insert(0, 1, ' ');
|
||||||
|
|
||||||
|
prompt_llama = ::replace(prompt_llama, "{1}", name_ni);
|
||||||
|
prompt_llama = ::replace(prompt_llama, "{10}", k_participants.at(0));
|
||||||
|
prompt_llama = ::replace(prompt_llama, "{11}", k_participants.at(1));
|
||||||
|
prompt_llama = ::replace(prompt_llama, "{12}", k_participants.at(2));
|
||||||
|
prompt_llama = ::replace(prompt_llama, "{13}", k_participants.at(3));
|
||||||
|
|
||||||
|
{
|
||||||
|
// get date string
|
||||||
|
std::string date_str;
|
||||||
|
{
|
||||||
|
time_t t = time(0);
|
||||||
|
struct tm * now = localtime(&t);
|
||||||
|
char buf[128];
|
||||||
|
strftime(buf, sizeof(buf), "%d/%m/%Y", now);
|
||||||
|
date_str = buf;
|
||||||
|
}
|
||||||
|
prompt_llama = ::replace(prompt_llama, "{1}", date_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// get time string
|
||||||
|
std::string time_str;
|
||||||
|
{
|
||||||
|
time_t t = time(0);
|
||||||
|
struct tm * now = localtime(&t);
|
||||||
|
char buf[128];
|
||||||
|
strftime(buf, sizeof(buf), "%H:%M", now);
|
||||||
|
time_str = buf;
|
||||||
|
}
|
||||||
|
prompt_llama = ::replace(prompt_llama, "{2}", time_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// get year string
|
||||||
|
std::string year_str;
|
||||||
|
{
|
||||||
|
time_t t = time(0);
|
||||||
|
struct tm * now = localtime(&t);
|
||||||
|
char buf[128];
|
||||||
|
strftime(buf, sizeof(buf), "%Y", now);
|
||||||
|
year_str = buf;
|
||||||
|
}
|
||||||
|
prompt_llama = ::replace(prompt_llama, "{3}", year_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_llama = ::replace(prompt_llama, "{4}", chat_symb);
|
||||||
|
|
||||||
|
// evaluate the initial prompt
|
||||||
|
|
||||||
|
auto embd_inp = ::llama_tokenize(ctx_llama, prompt_llama, true);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("%s : initializing - please wait ...\n", __func__);
|
||||||
|
|
||||||
|
if (llama_eval(ctx_llama, embd_inp.data(), embd_inp.size(), 0, params.n_threads)) {
|
||||||
|
fprintf(stderr, "%s : failed to eval\n", __func__);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.verbose_prompt) {
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
fprintf(stdout, "%s", prompt_whisper.c_str());
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
fprintf(stdout, "%s", prompt_llama.c_str());
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s : done! start speaking in the microphone\n", __func__);
|
||||||
|
printf("\n");
|
||||||
|
printf("%s%s", name_ni.c_str(), chat_symb.c_str());
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
// clear audio buffer
|
||||||
|
audio.clear();
|
||||||
|
|
||||||
|
// text inference variables
|
||||||
|
const int voice_id = params.voice_id;
|
||||||
|
const int n_keep = embd_inp.size();
|
||||||
|
const int n_ctx = llama_n_ctx(ctx_llama);
|
||||||
|
|
||||||
|
int n_past = n_keep;
|
||||||
|
int n_prev = 64; // TODO arg
|
||||||
|
|
||||||
|
std::vector<llama_token> embd;
|
||||||
|
|
||||||
|
// reverse prompts for detecting when it's time to stop speaking
|
||||||
|
std::vector<std::string> antiprompts = {
|
||||||
|
name_ni + chat_symb,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto & p : k_participants) {
|
||||||
|
antiprompts.push_back(p + chat_symb);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string text_heard_all;
|
||||||
|
|
||||||
|
// main loop
|
||||||
|
while (is_running) {
|
||||||
|
// handle Ctrl + C
|
||||||
|
is_running = sdl_poll_events();
|
||||||
|
|
||||||
|
if (!is_running) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// delay
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
int64_t t_ms = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
audio.get(15000, pcmf32_cur);
|
||||||
|
|
||||||
|
if (::vad_simple(pcmf32_cur, WHISPER_SAMPLE_RATE, 1250, params.vad_thold, params.freq_thold, params.print_energy) || force_speak) {
|
||||||
|
//fprintf(stdout, "%s: Speech detected! Processing ...\n", __func__);
|
||||||
|
|
||||||
|
audio.get(params.voice_ms, pcmf32_cur);
|
||||||
|
|
||||||
|
std::string text_heard;
|
||||||
|
|
||||||
|
if (!force_speak) {
|
||||||
|
text_heard = ::trim(::transcribe(ctx_wsp, params, pcmf32_cur, prompt_whisper, prob0, t_ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove text between brackets using regex
|
||||||
|
{
|
||||||
|
std::regex re("\\[.*?\\]");
|
||||||
|
text_heard = std::regex_replace(text_heard, re, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove text between brackets using regex
|
||||||
|
{
|
||||||
|
std::regex re("\\(.*?\\)");
|
||||||
|
text_heard = std::regex_replace(text_heard, re, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove all characters, except for letters, numbers, punctuation and ':', '\'', '-', ' '
|
||||||
|
text_heard = std::regex_replace(text_heard, std::regex("[^a-zA-Z0-9\\.,\\?!\\s\\:\\'\\-]"), "");
|
||||||
|
|
||||||
|
// take first line
|
||||||
|
text_heard = text_heard.substr(0, text_heard.find_first_of('\n'));
|
||||||
|
|
||||||
|
// remove leading and trailing whitespace
|
||||||
|
text_heard = std::regex_replace(text_heard, std::regex("^\\s+"), "");
|
||||||
|
text_heard = std::regex_replace(text_heard, std::regex("\\s+$"), "");
|
||||||
|
|
||||||
|
const std::vector<llama_token> tokens = llama_tokenize(ctx_llama, text_heard.c_str(), false);
|
||||||
|
|
||||||
|
if (text_heard.empty() || tokens.empty() || force_speak) {
|
||||||
|
//fprintf(stdout, "%s: Heard nothing, skipping ...\n", __func__);
|
||||||
|
audio.clear();
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
force_speak = false;
|
||||||
|
|
||||||
|
if (text_heard[0] != ' ') {
|
||||||
|
text_heard.insert(0, 1, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace homophones
|
||||||
|
for (const auto & homophone : k_homophones) {
|
||||||
|
for (const auto & word : homophone.second) {
|
||||||
|
text_heard = ::replace(text_heard, word, homophone.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check which participant was mentioned
|
||||||
|
const auto name_ref_old = name_ref;
|
||||||
|
for (const auto & participant : k_participants) {
|
||||||
|
if (participant == name_ref) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text_heard.find(participant) != std::string::npos) {
|
||||||
|
name_ref = participant;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (name_ref == name_ref_old && name_ref != name_ai) {
|
||||||
|
name_ref = name_ni;
|
||||||
|
}
|
||||||
|
|
||||||
|
text_heard += "\n" + name_ref + chat_symb;
|
||||||
|
fprintf(stdout, "%s%s%s", "\033[1m", text_heard.c_str(), "\033[0m");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
text_heard_all += text_heard;
|
||||||
|
// keep only last 100 characters
|
||||||
|
if (text_heard_all.size() > 100) {
|
||||||
|
text_heard_all = text_heard_all.substr(text_heard_all.size() - 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name_ref != name_ai) {
|
||||||
|
} else {
|
||||||
|
// text inference
|
||||||
|
bool done = false;
|
||||||
|
std::string text_to_speak;
|
||||||
|
|
||||||
|
embd = ::llama_tokenize(ctx_llama, text_heard_all, false);
|
||||||
|
text_heard_all.clear();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// predict
|
||||||
|
if (embd.size() > 0) {
|
||||||
|
if (n_past + (int) embd.size() > n_ctx) {
|
||||||
|
n_past = n_keep;
|
||||||
|
|
||||||
|
// insert n_left/2 tokens at the start of embd from last_n_tokens
|
||||||
|
embd.insert(embd.begin(), embd_inp.begin() + embd_inp.size() - n_prev, embd_inp.end());
|
||||||
|
|
||||||
|
//printf("\n---\n");
|
||||||
|
//printf("resetting: '");
|
||||||
|
//for (int i = 0; i < (int) embd.size(); i++) {
|
||||||
|
// printf("%s", llama_token_to_str(ctx_llama, embd[i]));
|
||||||
|
//}
|
||||||
|
//printf("'\n");
|
||||||
|
//printf("\n---\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llama_eval(ctx_llama, embd.data(), embd.size(), n_past, params.n_threads)) {
|
||||||
|
fprintf(stderr, "%s : failed to eval\n", __func__);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("n_iter = %d, n_past = %d, n_ctx = %d, n_keep = %d, n_prev = %d, embd.size() = %d\n", n_iter, n_past, n_ctx, n_keep, n_prev, (int) embd.size());
|
||||||
|
|
||||||
|
embd_inp.insert(embd_inp.end(), embd.begin(), embd.end());
|
||||||
|
n_past += embd.size();
|
||||||
|
embd.clear();
|
||||||
|
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
|
{
|
||||||
|
// out of user input, sample next token
|
||||||
|
const float top_k = 5;
|
||||||
|
const float top_p = 0.80f;
|
||||||
|
const float temp = 0.20f;
|
||||||
|
const float repeat_penalty = 1.0764f;
|
||||||
|
|
||||||
|
const int repeat_last_n = 256;
|
||||||
|
|
||||||
|
llama_token id = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto logits = llama_get_logits(ctx_llama);
|
||||||
|
logits[llama_token_eos()] = 0;
|
||||||
|
|
||||||
|
id = llama_sample_top_p_top_k(ctx_llama,
|
||||||
|
embd_inp.data() + std::max(0, n_past - repeat_last_n),
|
||||||
|
repeat_last_n, top_k, top_p, temp, repeat_penalty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id != llama_token_eos()) {
|
||||||
|
// add it to the context
|
||||||
|
embd.push_back(id);
|
||||||
|
|
||||||
|
text_to_speak += llama_token_to_str(ctx_llama, id);
|
||||||
|
|
||||||
|
printf("%s", llama_token_to_str(ctx_llama, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// new line
|
||||||
|
if (id == 13) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::string last_output;
|
||||||
|
for (int i = embd_inp.size() - 16; i < (int) embd_inp.size(); i++) {
|
||||||
|
last_output += llama_token_to_str(ctx_llama, embd_inp[i]);
|
||||||
|
}
|
||||||
|
last_output += llama_token_to_str(ctx_llama, embd[0]);
|
||||||
|
|
||||||
|
for (const std::string & antiprompt : antiprompts) {
|
||||||
|
if (last_output.find(antiprompt.c_str(), last_output.length() - antiprompt.length(), antiprompt.length()) != std::string::npos) {
|
||||||
|
done = true;
|
||||||
|
text_to_speak = ::replace(text_to_speak, antiprompt, "");
|
||||||
|
fflush(stdout);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_running = sdl_poll_events();
|
||||||
|
|
||||||
|
if (!is_running) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text_to_speak = ::replace(text_to_speak, "\"", "");
|
||||||
|
system((params.speak + " " + std::to_string(voice_id) + " \"" + text_to_speak + "\"").c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
audio.clear();
|
||||||
|
|
||||||
|
++n_iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audio.pause();
|
||||||
|
|
||||||
|
whisper_print_timings(ctx_wsp);
|
||||||
|
whisper_free(ctx_wsp);
|
||||||
|
|
||||||
|
llama_print_timings(ctx_llama);
|
||||||
|
llama_free(ctx_llama);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
if (WHISPER_SUPPORT_SDL2)
|
|
||||||
# talk.llama
|
|
||||||
set(TARGET talk-llama)
|
|
||||||
|
|
||||||
# TODO: this is temporary
|
|
||||||
# need to export ggml symbols for MSVC, but too lazy ..
|
|
||||||
add_executable(${TARGET} talk-llama.cpp llama.cpp)
|
|
||||||
|
|
||||||
include(DefaultTargetOptions)
|
|
||||||
|
|
||||||
target_link_libraries(${TARGET} PRIVATE common common-sdl whisper ${SDL2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
endif ()
|
|
@ -1,2 +0,0 @@
|
|||||||
# talk.llama
|
|
||||||
|
|
@ -1,511 +0,0 @@
|
|||||||
// Talk with AI
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "common-sdl.h"
|
|
||||||
#include "whisper.h"
|
|
||||||
#include "llama.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <fstream>
|
|
||||||
#include <regex>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
#include <regex>
|
|
||||||
|
|
||||||
std::vector<llama_token> llama_tokenize(struct llama_context * ctx, const std::string & text, bool add_bos) {
|
|
||||||
// initialize to prompt numer of chars, since n_tokens <= n_prompt_chars
|
|
||||||
std::vector<llama_token> res(text.size() + (int)add_bos);
|
|
||||||
int n = llama_tokenize(ctx, text.c_str(), res.data(), res.size(), add_bos);
|
|
||||||
assert(n >= 0);
|
|
||||||
res.resize(n);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// command-line parameters
|
|
||||||
struct whisper_params {
|
|
||||||
int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
|
|
||||||
int32_t voice_ms = 10000;
|
|
||||||
int32_t capture_id = -1;
|
|
||||||
int32_t max_tokens = 32;
|
|
||||||
int32_t audio_ctx = 0;
|
|
||||||
|
|
||||||
float vad_thold = 0.6f;
|
|
||||||
float freq_thold = 100.0f;
|
|
||||||
|
|
||||||
bool speed_up = false;
|
|
||||||
bool translate = false;
|
|
||||||
bool print_special = false;
|
|
||||||
bool print_energy = false;
|
|
||||||
bool no_timestamps = true;
|
|
||||||
|
|
||||||
std::string person = "Santa";
|
|
||||||
std::string language = "en";
|
|
||||||
std::string model_wsp = "models/ggml-base.en.bin";
|
|
||||||
std::string model_llama = "models/ggml-llama-7B.bin";
|
|
||||||
std::string speak = "./examples/talk/speak.sh";
|
|
||||||
std::string fname_out;
|
|
||||||
};
|
|
||||||
|
|
||||||
void whisper_print_usage(int argc, char ** argv, const whisper_params & params);
|
|
||||||
|
|
||||||
bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|
||||||
for (int i = 1; i < argc; i++) {
|
|
||||||
std::string arg = argv[i];
|
|
||||||
|
|
||||||
if (arg == "-h" || arg == "--help") {
|
|
||||||
whisper_print_usage(argc, argv, params);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
else if (arg == "-t" || arg == "--threads") { params.n_threads = std::stoi(argv[++i]); }
|
|
||||||
else if (arg == "-vms" || arg == "--voice-ms") { params.voice_ms = std::stoi(argv[++i]); }
|
|
||||||
else if (arg == "-c" || arg == "--capture") { params.capture_id = std::stoi(argv[++i]); }
|
|
||||||
else if (arg == "-mt" || arg == "--max-tokens") { params.max_tokens = std::stoi(argv[++i]); }
|
|
||||||
else if (arg == "-ac" || arg == "--audio-ctx") { params.audio_ctx = std::stoi(argv[++i]); }
|
|
||||||
else if (arg == "-vth" || arg == "--vad-thold") { params.vad_thold = std::stof(argv[++i]); }
|
|
||||||
else if (arg == "-fth" || arg == "--freq-thold") { params.freq_thold = std::stof(argv[++i]); }
|
|
||||||
else if (arg == "-su" || arg == "--speed-up") { params.speed_up = true; }
|
|
||||||
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
|
|
||||||
else if (arg == "-ps" || arg == "--print-special") { params.print_special = true; }
|
|
||||||
else if (arg == "-pe" || arg == "--print-energy") { params.print_energy = true; }
|
|
||||||
else if (arg == "-p" || arg == "--person") { params.person = argv[++i]; }
|
|
||||||
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
|
|
||||||
else if (arg == "-mw" || arg == "--model-whisper") { params.model_wsp = argv[++i]; }
|
|
||||||
else if (arg == "-ml" || arg == "--model-llama") { params.model_llama = argv[++i]; }
|
|
||||||
else if (arg == "-s" || arg == "--speak") { params.speak = argv[++i]; }
|
|
||||||
else if (arg == "-f" || arg == "--file") { params.fname_out = argv[++i]; }
|
|
||||||
else {
|
|
||||||
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
|
||||||
whisper_print_usage(argc, argv, params);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & params) {
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fprintf(stderr, "usage: %s [options]\n", argv[0]);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fprintf(stderr, "options:\n");
|
|
||||||
fprintf(stderr, " -h, --help [default] show this help message and exit\n");
|
|
||||||
fprintf(stderr, " -t N, --threads N [%-7d] number of threads to use during computation\n", params.n_threads);
|
|
||||||
fprintf(stderr, " -vms N, --voice-ms N [%-7d] voice duration in milliseconds\n", params.voice_ms);
|
|
||||||
fprintf(stderr, " -c ID, --capture ID [%-7d] capture device ID\n", params.capture_id);
|
|
||||||
fprintf(stderr, " -mt N, --max-tokens N [%-7d] maximum number of tokens per audio chunk\n", params.max_tokens);
|
|
||||||
fprintf(stderr, " -ac N, --audio-ctx N [%-7d] audio context size (0 - all)\n", params.audio_ctx);
|
|
||||||
fprintf(stderr, " -vth N, --vad-thold N [%-7.2f] voice activity detection threshold\n", params.vad_thold);
|
|
||||||
fprintf(stderr, " -fth N, --freq-thold N [%-7.2f] high-pass frequency cutoff\n", params.freq_thold);
|
|
||||||
fprintf(stderr, " -su, --speed-up [%-7s] speed up audio by x2 (reduced accuracy)\n", params.speed_up ? "true" : "false");
|
|
||||||
fprintf(stderr, " -tr, --translate [%-7s] translate from source language to english\n", params.translate ? "true" : "false");
|
|
||||||
fprintf(stderr, " -ps, --print-special [%-7s] print special tokens\n", params.print_special ? "true" : "false");
|
|
||||||
fprintf(stderr, " -pe, --print-energy [%-7s] print sound energy (for debugging)\n", params.print_energy ? "true" : "false");
|
|
||||||
fprintf(stderr, " -p NAME, --person NAME [%-7s] person name (for prompt selection)\n", params.person.c_str());
|
|
||||||
fprintf(stderr, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str());
|
|
||||||
fprintf(stderr, " -mw FILE, --model-whisper [%-7s] whisper model file\n", params.model_wsp.c_str());
|
|
||||||
fprintf(stderr, " -mg FILE, --model-llama [%-7s] llama model file\n", params.model_llama.c_str());
|
|
||||||
fprintf(stderr, " -s FILE, --speak TEXT [%-7s] command for TTS\n", params.speak.c_str());
|
|
||||||
fprintf(stderr, " -f FNAME, --file FNAME [%-7s] text output file name\n", params.fname_out.c_str());
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string transcribe(whisper_context * ctx, const whisper_params & params, const std::vector<float> & pcmf32, float & prob, int64_t & t_ms) {
|
|
||||||
const auto t_start = std::chrono::high_resolution_clock::now();
|
|
||||||
|
|
||||||
prob = 0.0f;
|
|
||||||
t_ms = 0;
|
|
||||||
|
|
||||||
whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY);
|
|
||||||
|
|
||||||
wparams.print_progress = false;
|
|
||||||
wparams.print_special = params.print_special;
|
|
||||||
wparams.print_realtime = false;
|
|
||||||
wparams.print_timestamps = !params.no_timestamps;
|
|
||||||
wparams.translate = params.translate;
|
|
||||||
wparams.no_context = true;
|
|
||||||
wparams.single_segment = true;
|
|
||||||
wparams.max_tokens = params.max_tokens;
|
|
||||||
wparams.language = params.language.c_str();
|
|
||||||
wparams.n_threads = params.n_threads;
|
|
||||||
|
|
||||||
wparams.audio_ctx = params.audio_ctx;
|
|
||||||
wparams.speed_up = params.speed_up;
|
|
||||||
|
|
||||||
if (whisper_full(ctx, wparams, pcmf32.data(), pcmf32.size()) != 0) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
int prob_n = 0;
|
|
||||||
std::string result;
|
|
||||||
|
|
||||||
const int n_segments = whisper_full_n_segments(ctx);
|
|
||||||
for (int i = 0; i < n_segments; ++i) {
|
|
||||||
const char * text = whisper_full_get_segment_text(ctx, i);
|
|
||||||
|
|
||||||
result += text;
|
|
||||||
|
|
||||||
const int n_tokens = whisper_full_n_tokens(ctx, i);
|
|
||||||
for (int j = 0; j < n_tokens; ++j) {
|
|
||||||
const auto token = whisper_full_get_token_data(ctx, i, j);
|
|
||||||
|
|
||||||
prob += token.p;
|
|
||||||
++prob_n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prob_n > 0) {
|
|
||||||
prob /= prob_n;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto t_end = std::chrono::high_resolution_clock::now();
|
|
||||||
t_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t_end - t_start).count();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// need to have leading ' '
|
|
||||||
//const std::string k_prompt = R"( Transcript of a dialog, where {1} interacts with an Assistant named Bob. Bob is helpful, kind, honest, good at writing, and never fails to answer {1}'s requests immediately and with precision.
|
|
||||||
//
|
|
||||||
//{0}: Hello, Bob.
|
|
||||||
//{1}: Hello {0}. How may I help you today?
|
|
||||||
//{0}:)";
|
|
||||||
|
|
||||||
const std::string k_prompt = R"( Text transcript of a never ending dialog, where {0} interacts with an AI assistant named {1}.
|
|
||||||
{1} is helpful, kind, honest, friendly, good at writing and never fails to answer {0}’s requests immediately and with details and precision.
|
|
||||||
There are no annotations like (30 seconds passed...) or (to himself), just what {0} and {1} say aloud to each other.
|
|
||||||
The transcript only includes text, it does not include markup like HTML and Markdown.
|
|
||||||
{1} answers responds with short and concise answers.
|
|
||||||
|
|
||||||
{0}{4} Hello, {1}!
|
|
||||||
{1}{4} Hello {0}! How may I help you today?
|
|
||||||
{0}{4} What time is it?
|
|
||||||
{1}{4} It is {2} o'clock.
|
|
||||||
{0}{4} What year is it?
|
|
||||||
{1}{4} We are in {3}.
|
|
||||||
{0}{4} What is a cat?
|
|
||||||
{1}{4} A cat is a domestic species of small carnivorous mammal. It is the only domesticated species in the family Felidae.
|
|
||||||
{0}{4} Name a color.
|
|
||||||
{1}{4} Blue
|
|
||||||
{0}{4})";
|
|
||||||
|
|
||||||
int main(int argc, char ** argv) {
|
|
||||||
whisper_params params;
|
|
||||||
|
|
||||||
if (whisper_params_parse(argc, argv, params) == false) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (whisper_lang_id(params.language.c_str()) == -1) {
|
|
||||||
fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str());
|
|
||||||
whisper_print_usage(argc, argv, params);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// whisper init
|
|
||||||
|
|
||||||
struct whisper_context * ctx_wsp = whisper_init_from_file(params.model_wsp.c_str());
|
|
||||||
|
|
||||||
// llama init
|
|
||||||
|
|
||||||
auto lparams = llama_context_default_params();
|
|
||||||
|
|
||||||
lparams.n_ctx = 512;
|
|
||||||
lparams.n_parts = 2; // TODO fix
|
|
||||||
lparams.seed = 1; // TODO fix
|
|
||||||
lparams.f16_kv = true;
|
|
||||||
|
|
||||||
struct llama_context * ctx_llama = llama_init_from_file(params.model_llama.c_str(), lparams);
|
|
||||||
|
|
||||||
// print some info about the processing
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
if (!whisper_is_multilingual(ctx_wsp)) {
|
|
||||||
if (params.language != "en" || params.translate) {
|
|
||||||
params.language = "en";
|
|
||||||
params.translate = false;
|
|
||||||
fprintf(stderr, "%s: WARNING: model is not multilingual, ignoring language and translation options\n", __func__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf(stderr, "%s: processing, %d threads, lang = %s, task = %s, timestamps = %d ...\n",
|
|
||||||
__func__,
|
|
||||||
params.n_threads,
|
|
||||||
params.language.c_str(),
|
|
||||||
params.translate ? "translate" : "transcribe",
|
|
||||||
params.no_timestamps ? 0 : 1);
|
|
||||||
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// init audio
|
|
||||||
|
|
||||||
audio_async audio(30*1000);
|
|
||||||
if (!audio.init(params.capture_id, WHISPER_SAMPLE_RATE)) {
|
|
||||||
fprintf(stderr, "%s: audio.init() failed!\n", __func__);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
audio.resume();
|
|
||||||
|
|
||||||
int n_iter = 0;
|
|
||||||
|
|
||||||
bool is_running = true;
|
|
||||||
bool force_speak = false;
|
|
||||||
|
|
||||||
float prob0 = 0.0f;
|
|
||||||
|
|
||||||
const std::string chat_symb = ":";
|
|
||||||
const std::string bot_name = "LLAMA";
|
|
||||||
|
|
||||||
std::vector<float> pcmf32_cur;
|
|
||||||
std::vector<float> pcmf32_prompt;
|
|
||||||
|
|
||||||
std::string prompt_org = k_prompt;
|
|
||||||
prompt_org = ::replace(prompt_org, "{0}", params.person);
|
|
||||||
prompt_org = ::replace(prompt_org, "{1}", bot_name);
|
|
||||||
|
|
||||||
{
|
|
||||||
// get time string
|
|
||||||
std::string time_str;
|
|
||||||
{
|
|
||||||
time_t t = time(0);
|
|
||||||
struct tm * now = localtime(&t);
|
|
||||||
char buf[128];
|
|
||||||
strftime(buf, sizeof(buf), "%H:%M", now);
|
|
||||||
time_str = buf;
|
|
||||||
}
|
|
||||||
prompt_org = ::replace(prompt_org, "{2}", time_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// get year string
|
|
||||||
std::string year_str;
|
|
||||||
{
|
|
||||||
time_t t = time(0);
|
|
||||||
struct tm * now = localtime(&t);
|
|
||||||
char buf[128];
|
|
||||||
strftime(buf, sizeof(buf), "%Y", now);
|
|
||||||
year_str = buf;
|
|
||||||
}
|
|
||||||
prompt_org = ::replace(prompt_org, "{3}", year_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt_org = ::replace(prompt_org, "{4}", chat_symb);
|
|
||||||
|
|
||||||
auto embd_inp = ::llama_tokenize(ctx_llama, prompt_org, true);
|
|
||||||
|
|
||||||
const int n_ctx = llama_n_ctx(ctx_llama);
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
printf("%s : initializing - please wait ...\n", __func__);
|
|
||||||
|
|
||||||
if (llama_eval(ctx_llama, embd_inp.data(), embd_inp.size(), 0, params.n_threads)) {
|
|
||||||
fprintf(stderr, "%s : failed to eval\n", __func__);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//fprintf(stdout, "\n");
|
|
||||||
//fprintf(stdout, "%s", prompt_org.c_str());
|
|
||||||
//fflush(stdout);
|
|
||||||
|
|
||||||
printf("%s : done! start speaking in the microphone\n", __func__);
|
|
||||||
printf("\n");
|
|
||||||
printf("%s%s", params.person.c_str(), chat_symb.c_str());
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
audio.clear();
|
|
||||||
|
|
||||||
const int n_keep = embd_inp.size();
|
|
||||||
const int voice_id = 2;
|
|
||||||
|
|
||||||
int n_past = n_keep;
|
|
||||||
int n_prev = 64; // TODO arg
|
|
||||||
|
|
||||||
std::vector<llama_token> embd;
|
|
||||||
|
|
||||||
std::vector<std::string> antiprompts = {
|
|
||||||
params.person + chat_symb,
|
|
||||||
};
|
|
||||||
|
|
||||||
// main loop
|
|
||||||
while (is_running) {
|
|
||||||
// handle Ctrl + C
|
|
||||||
is_running = sdl_poll_events();
|
|
||||||
|
|
||||||
if (!is_running) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// delay
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
|
|
||||||
int64_t t_ms = 0;
|
|
||||||
|
|
||||||
{
|
|
||||||
audio.get(2000, pcmf32_cur);
|
|
||||||
|
|
||||||
if (::vad_simple(pcmf32_cur, WHISPER_SAMPLE_RATE, 1250, params.vad_thold, params.freq_thold, params.print_energy) || force_speak) {
|
|
||||||
//fprintf(stdout, "%s: Speech detected! Processing ...\n", __func__);
|
|
||||||
|
|
||||||
audio.get(params.voice_ms, pcmf32_cur);
|
|
||||||
|
|
||||||
std::string text_heard;
|
|
||||||
|
|
||||||
if (!force_speak) {
|
|
||||||
text_heard = ::trim(::transcribe(ctx_wsp, params, pcmf32_cur, prob0, t_ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove text between brackets using regex
|
|
||||||
{
|
|
||||||
std::regex re("\\[.*?\\]");
|
|
||||||
text_heard = std::regex_replace(text_heard, re, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove text between brackets using regex
|
|
||||||
{
|
|
||||||
std::regex re("\\(.*?\\)");
|
|
||||||
text_heard = std::regex_replace(text_heard, re, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove all characters, except for letters, numbers, punctuation and ':', '\'', '-', ' '
|
|
||||||
text_heard = std::regex_replace(text_heard, std::regex("[^a-zA-Z0-9\\.,\\?!\\s\\:\\'\\-]"), "");
|
|
||||||
|
|
||||||
// take first line
|
|
||||||
text_heard = text_heard.substr(0, text_heard.find_first_of('\n'));
|
|
||||||
|
|
||||||
// remove leading and trailing whitespace
|
|
||||||
text_heard = std::regex_replace(text_heard, std::regex("^\\s+"), "");
|
|
||||||
text_heard = std::regex_replace(text_heard, std::regex("\\s+$"), "");
|
|
||||||
|
|
||||||
const std::vector<llama_token> tokens = llama_tokenize(ctx_llama, text_heard.c_str(), false);
|
|
||||||
|
|
||||||
if (text_heard.empty() || tokens.empty() || force_speak) {
|
|
||||||
//fprintf(stdout, "%s: Heard nothing, skipping ...\n", __func__);
|
|
||||||
audio.clear();
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
force_speak = false;
|
|
||||||
|
|
||||||
text_heard.insert(0, 1, ' ');
|
|
||||||
text_heard += "\n" + bot_name + chat_symb;
|
|
||||||
fprintf(stdout, "%s%s%s", "\033[1m", text_heard.c_str(), "\033[0m");
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
embd = ::llama_tokenize(ctx_llama, text_heard, false);
|
|
||||||
|
|
||||||
// text inference
|
|
||||||
bool done = false;
|
|
||||||
std::string text_to_speak;
|
|
||||||
while (true) {
|
|
||||||
// predict
|
|
||||||
if (embd.size() > 0) {
|
|
||||||
if (n_past + (int) embd.size() > n_ctx) {
|
|
||||||
n_past = n_keep;
|
|
||||||
|
|
||||||
// insert n_left/2 tokens at the start of embd from last_n_tokens
|
|
||||||
embd.insert(embd.begin(), embd_inp.begin() + embd_inp.size() - n_prev, embd_inp.end());
|
|
||||||
|
|
||||||
//printf("\n---\n");
|
|
||||||
//printf("resetting: '");
|
|
||||||
//for (int i = 0; i < (int) embd.size(); i++) {
|
|
||||||
// printf("%s", llama_token_to_str(ctx_llama, embd[i]));
|
|
||||||
//}
|
|
||||||
//printf("'\n");
|
|
||||||
//printf("\n---\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llama_eval(ctx_llama, embd.data(), embd.size(), n_past, params.n_threads)) {
|
|
||||||
fprintf(stderr, "%s : failed to eval\n", __func__);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//printf("n_iter = %d, n_past = %d, n_ctx = %d, n_keep = %d, n_prev = %d, embd.size() = %d\n", n_iter, n_past, n_ctx, n_keep, n_prev, (int) embd.size());
|
|
||||||
|
|
||||||
embd_inp.insert(embd_inp.end(), embd.begin(), embd.end());
|
|
||||||
n_past += embd.size();
|
|
||||||
embd.clear();
|
|
||||||
|
|
||||||
if (done) break;
|
|
||||||
|
|
||||||
{
|
|
||||||
// out of user input, sample next token
|
|
||||||
const float top_k = 5;
|
|
||||||
const float top_p = 0.80f;
|
|
||||||
const float temp = 0.30f;
|
|
||||||
const float repeat_penalty = 1.1764f;
|
|
||||||
|
|
||||||
const int repeat_last_n = 256;
|
|
||||||
|
|
||||||
llama_token id = 0;
|
|
||||||
|
|
||||||
{
|
|
||||||
//auto logits = llama_get_logits(ctx_llama);
|
|
||||||
//logits[llama_token_eos()] = 0;
|
|
||||||
|
|
||||||
id = llama_sample_top_p_top_k(ctx_llama,
|
|
||||||
embd_inp.data() + std::max(0, n_past - repeat_last_n),
|
|
||||||
repeat_last_n, top_k, top_p, temp, repeat_penalty);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id != llama_token_eos()) {
|
|
||||||
// add it to the context
|
|
||||||
embd.push_back(id);
|
|
||||||
|
|
||||||
text_to_speak += llama_token_to_str(ctx_llama, id);
|
|
||||||
|
|
||||||
printf("%s", llama_token_to_str(ctx_llama, id));
|
|
||||||
} else {
|
|
||||||
// TODO
|
|
||||||
printf("EOS TOKEN - SHOULD NOT HAPPEN\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::string last_output;
|
|
||||||
for (int i = embd_inp.size() - 16; i < (int) embd_inp.size(); i++) {
|
|
||||||
last_output += llama_token_to_str(ctx_llama, embd_inp[i]);
|
|
||||||
}
|
|
||||||
last_output += llama_token_to_str(ctx_llama, embd[0]);
|
|
||||||
|
|
||||||
for (std::string & antiprompt : antiprompts) {
|
|
||||||
if (last_output.find(antiprompt.c_str(), last_output.length() - antiprompt.length(), antiprompt.length()) != std::string::npos) {
|
|
||||||
done = true;
|
|
||||||
text_to_speak = ::replace(text_to_speak, antiprompt, "");
|
|
||||||
fflush(stdout);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is_running = sdl_poll_events();
|
|
||||||
|
|
||||||
if (!is_running) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text_to_speak = ::replace(text_to_speak, "\"", "");
|
|
||||||
system((params.speak + " " + std::to_string(voice_id) + " \"" + text_to_speak + "\"").c_str());
|
|
||||||
|
|
||||||
audio.clear();
|
|
||||||
|
|
||||||
++n_iter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
audio.pause();
|
|
||||||
|
|
||||||
whisper_print_timings(ctx_wsp);
|
|
||||||
whisper_free(ctx_wsp);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -325,9 +325,12 @@ bool gpt2_model_load(const std::string & fname, gpt2_model & model, gpt_vocab &
|
|||||||
|
|
||||||
// create the ggml context
|
// create the ggml context
|
||||||
{
|
{
|
||||||
struct ggml_init_params params;
|
struct ggml_init_params params = {
|
||||||
params.mem_size = ctx_size;
|
/*.mem_size =*/ ctx_size,
|
||||||
params.mem_buffer = NULL;
|
/*.mem_buffer =*/ nullptr,
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
model.ctx = ggml_init(params);
|
model.ctx = ggml_init(params);
|
||||||
if (!model.ctx) {
|
if (!model.ctx) {
|
||||||
@ -528,9 +531,11 @@ bool gpt2_eval(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ggml_init_params params;
|
struct ggml_init_params params = {
|
||||||
params.mem_size = buf_size;
|
/*.mem_size =*/ buf_size,
|
||||||
params.mem_buffer = buf;
|
/*.mem_buffer =*/ buf,
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
|
};
|
||||||
|
|
||||||
struct ggml_context * ctx0 = ggml_init(params);
|
struct ggml_context * ctx0 = ggml_init(params);
|
||||||
|
|
||||||
|
@ -325,9 +325,11 @@ bool gpt2_model_load(const std::string & fname, gpt2_model & model, gpt_vocab &
|
|||||||
|
|
||||||
// create the ggml context
|
// create the ggml context
|
||||||
{
|
{
|
||||||
struct ggml_init_params params;
|
struct ggml_init_params params = {
|
||||||
params.mem_size = ctx_size;
|
/*.mem_size =*/ ctx_size,
|
||||||
params.mem_buffer = nullptr;
|
/*.mem_buffer =*/ nullptr,
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
|
};
|
||||||
|
|
||||||
model.ctx = ggml_init(params);
|
model.ctx = ggml_init(params);
|
||||||
if (!model.ctx) {
|
if (!model.ctx) {
|
||||||
@ -528,9 +530,11 @@ bool gpt2_eval(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ggml_init_params params;
|
struct ggml_init_params params = {
|
||||||
params.mem_size = buf_size;
|
/*.mem_size =*/ buf_size,
|
||||||
params.mem_buffer = buf;
|
/*.mem_buffer =*/ buf,
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
|
};
|
||||||
|
|
||||||
struct ggml_context * ctx0 = ggml_init(params);
|
struct ggml_context * ctx0 = ggml_init(params);
|
||||||
|
|
||||||
|
@ -7,7 +7,10 @@
|
|||||||
# Mac OS: brew install espeak
|
# Mac OS: brew install espeak
|
||||||
# Linux: apt-get install espeak
|
# Linux: apt-get install espeak
|
||||||
#
|
#
|
||||||
espeak -v en-us+m$1 -s 175 -p 50 -a 200 -g 5 -k 5 "$2"
|
#espeak -v en-us+m$1 -s 175 -p 50 -a 200 -g 5 -k 5 "$2"
|
||||||
|
|
||||||
|
# Mac OS "say" command
|
||||||
|
say "$2"
|
||||||
|
|
||||||
# Eleven Labs
|
# Eleven Labs
|
||||||
#
|
#
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
A sample SwiftUI app using [whisper.cpp](https://github.com/ggerganov/whisper.cpp/) to do voice-to-text transcriptions.
|
A sample SwiftUI app using [whisper.cpp](https://github.com/ggerganov/whisper.cpp/) to do voice-to-text transcriptions.
|
||||||
See also: [whisper.objc](https://github.com/ggerganov/whisper.cpp/tree/master/examples/whisper.objc).
|
See also: [whisper.objc](https://github.com/ggerganov/whisper.cpp/tree/master/examples/whisper.objc).
|
||||||
|
|
||||||
To use:
|
**Usage**:
|
||||||
|
|
||||||
1. Select a model from the [whisper.cpp repository](https://github.com/ggerganov/whisper.cpp/tree/master/models).[^1]
|
1. Select a model from the [whisper.cpp repository](https://github.com/ggerganov/whisper.cpp/tree/master/models).[^1]
|
||||||
2. Add the model to "whisper.swiftui.demo/Resources/models" via Xcode.
|
2. Add the model to `whisper.swiftui.demo/Resources/models` **via Xcode**.
|
||||||
3. Select a sample audio file (for example, [jfk.wav](https://github.com/ggerganov/whisper.cpp/raw/master/samples/jfk.wav)).
|
3. Select a sample audio file (for example, [jfk.wav](https://github.com/ggerganov/whisper.cpp/raw/master/samples/jfk.wav)).
|
||||||
4. Add the model to "whisper.swiftui.demo/Resources/samples" via Xcode.
|
4. Add the sample audio file to `whisper.swiftui.demo/Resources/samples` **via Xcode**.
|
||||||
5. Select the "Release" [^2] build configuration under "Run", then deploy and run to your device.
|
5. Select the "Release" [^2] build configuration under "Run", then deploy and run to your device.
|
||||||
|
|
||||||
|
**Note:** Pay attention to the folder path: `whisper.swiftui.demo/Resources/models` is the appropriate directory to place resources whilst `whisper.swiftui.demo/Models` is related to actual code.
|
||||||
|
|
||||||
[^1]: I recommend the tiny, base or small models for running on an iOS device.
|
[^1]: I recommend the tiny, base or small models for running on an iOS device.
|
||||||
|
|
||||||
[^2]: The `Release` build can boost performance of transcription. In this project, it also added `-O3 -DNDEBUG` to `Other C Flags`, but adding flags to app proj is not ideal in real world (applies to all C/C++ files), consider splitting xcodeproj in workspace in your own project.
|
[^2]: The `Release` build can boost performance of transcription. In this project, it also added `-O3 -DNDEBUG` to `Other C Flags`, but adding flags to app proj is not ideal in real world (applies to all C/C++ files), consider splitting xcodeproj in workspace in your own project.
|
||||||
|
|
||||||

|

|
||||||
|
11
ggml.h
11
ggml.h
@ -316,6 +316,7 @@ struct ggml_init_params {
|
|||||||
// memory pool
|
// memory pool
|
||||||
size_t mem_size; // bytes
|
size_t mem_size; // bytes
|
||||||
void * mem_buffer; // if NULL, memory will be allocated internally
|
void * mem_buffer; // if NULL, memory will be allocated internally
|
||||||
|
bool no_alloc; // don't allocate memory for the tensor data
|
||||||
};
|
};
|
||||||
|
|
||||||
void ggml_time_init(void); // call this once at the beginning of the program
|
void ggml_time_init(void); // call this once at the beginning of the program
|
||||||
@ -344,7 +345,11 @@ size_t ggml_used_mem(const struct ggml_context * ctx);
|
|||||||
size_t ggml_set_scratch(struct ggml_context * ctx, struct ggml_scratch scratch);
|
size_t ggml_set_scratch(struct ggml_context * ctx, struct ggml_scratch scratch);
|
||||||
|
|
||||||
bool ggml_mlock_supported(void);
|
bool ggml_mlock_supported(void);
|
||||||
bool ggml_mlock(struct ggml_context * ctx, char ** err_p);
|
bool ggml_mlock(
|
||||||
|
struct ggml_context * ctx,
|
||||||
|
const void *opt_extra_addr,
|
||||||
|
size_t opt_extra_len,
|
||||||
|
char **err_p);
|
||||||
|
|
||||||
struct ggml_tensor * ggml_new_tensor(
|
struct ggml_tensor * ggml_new_tensor(
|
||||||
struct ggml_context * ctx,
|
struct ggml_context * ctx,
|
||||||
@ -748,8 +753,8 @@ enum ggml_opt_result ggml_opt(
|
|||||||
// quantization
|
// quantization
|
||||||
//
|
//
|
||||||
|
|
||||||
size_t ggml_quantize_q4_0(const float * src, void * dst, int n, int k, int qk, int64_t * hist);
|
size_t ggml_quantize_q4_0(const float * src, void * dst, int n, int k, int64_t * hist);
|
||||||
size_t ggml_quantize_q4_1(const float * src, void * dst, int n, int k, int qk, int64_t * hist);
|
size_t ggml_quantize_q4_1(const float * src, void * dst, int n, int k, int64_t * hist);
|
||||||
|
|
||||||
//
|
//
|
||||||
// system info
|
// system info
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# This script downloads Whisper model files that have already been converted to Core ML format.
|
|
||||||
# This way you don't have to convert them yourself.
|
|
||||||
|
|
||||||
src="https://huggingface.co/datasets/ggerganov/whisper.cpp-coreml"
|
|
||||||
pfx="resolve/main/ggml"
|
|
||||||
|
|
||||||
# get the path of this script
|
|
||||||
function get_script_path() {
|
|
||||||
if [ -x "$(command -v realpath)" ]; then
|
|
||||||
echo "$(dirname $(realpath $0))"
|
|
||||||
else
|
|
||||||
local ret="$(cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P)"
|
|
||||||
echo "$ret"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
models_path="$(get_script_path)"
|
|
||||||
|
|
||||||
# Whisper models
|
|
||||||
models=( "tiny.en" "tiny" "base.en" "base" "small.en" "small" "medium.en" "medium" "large-v1" "large" )
|
|
||||||
|
|
||||||
# list available models
|
|
||||||
function list_models {
|
|
||||||
printf "\n"
|
|
||||||
printf " Available models:"
|
|
||||||
for model in "${models[@]}"; do
|
|
||||||
printf " $model"
|
|
||||||
done
|
|
||||||
printf "\n\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "$#" -ne 1 ]; then
|
|
||||||
printf "Usage: $0 <model>\n"
|
|
||||||
list_models
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
model=$1
|
|
||||||
|
|
||||||
if [[ ! " ${models[@]} " =~ " ${model} " ]]; then
|
|
||||||
printf "Invalid model: $model\n"
|
|
||||||
list_models
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# download Core ML model
|
|
||||||
|
|
||||||
printf "Downloading Core ML model $model from '$src' ...\n"
|
|
||||||
|
|
||||||
cd $models_path
|
|
||||||
|
|
||||||
if [ -f "ggml-$model.mlmodel" ]; then
|
|
||||||
printf "Model $model already exists. Skipping download.\n"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -x "$(command -v wget)" ]; then
|
|
||||||
wget --quiet --show-progress -O ggml-$model.mlmodel $src/$pfx-$model.mlmodel
|
|
||||||
elif [ -x "$(command -v curl)" ]; then
|
|
||||||
curl -L --output ggml-$model.mlmodel $src/$pfx-$model.mlmodel
|
|
||||||
else
|
|
||||||
printf "Either wget or curl is required to download models.\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
printf "Failed to download Core ML model $model \n"
|
|
||||||
printf "Please try again later or download the original Whisper model files and convert them yourself.\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf "Done! Model '$model' saved in 'models/ggml-$model.mlmodel'\n"
|
|
||||||
printf "Run the following command to compile it:\n\n"
|
|
||||||
printf " $ xcrun coremlc compile ./models/ggml-$model.mlmodel ./models\n\n"
|
|
||||||
printf "You can now use it like this:\n\n"
|
|
||||||
printf " $ ./main -m models/ggml-$model.bin -f samples/jfk.wav\n"
|
|
||||||
printf "\n"
|
|
@ -12,7 +12,7 @@ pfx="resolve/main/ggml"
|
|||||||
# get the path of this script
|
# get the path of this script
|
||||||
function get_script_path() {
|
function get_script_path() {
|
||||||
if [ -x "$(command -v realpath)" ]; then
|
if [ -x "$(command -v realpath)" ]; then
|
||||||
echo "$(dirname $(realpath $0))"
|
echo "$(dirname "$(realpath "$0")")"
|
||||||
else
|
else
|
||||||
local ret="$(cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P)"
|
local ret="$(cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P)"
|
||||||
echo "$ret"
|
echo "$ret"
|
||||||
|
6
talk-ggama.sh
Executable file
6
talk-ggama.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
./talk-llama \
|
||||||
|
-mw ./models/ggml-small.en.bin \
|
||||||
|
-ml ../llama.cpp/models/13B/ggml-model-q4_0.bin \
|
||||||
|
--name-ni "Georgi" \
|
||||||
|
--name-ai "GGaMA" \
|
||||||
|
-t 8 -vid 1 --speak ./examples/talk-llama/speak.sh
|
6
talk-llama.sh
Executable file
6
talk-llama.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
./talk-llama \
|
||||||
|
-mw ./models/ggml-small.en.bin \
|
||||||
|
-ml ../llama.cpp/models/13B/ggml-model-q4_0.bin \
|
||||||
|
--name-ni "Georgi" \
|
||||||
|
--name-ai "LLaMA" \
|
||||||
|
-t 8 -vid 0 --speak ./examples/talk-llama/speak.sh
|
6
talk-rrama.sh
Executable file
6
talk-rrama.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
./talk-llama \
|
||||||
|
-mw ./models/ggml-small.en.bin \
|
||||||
|
-ml ../llama.cpp/models/13B/ggml-model-q4_0.bin \
|
||||||
|
--name-ni "Georgi" \
|
||||||
|
--name-ai "RRaMA" \
|
||||||
|
-t 8 -vid 3 --speak ./examples/talk-llama/speak.sh
|
6
talk-ssama.sh
Executable file
6
talk-ssama.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
./talk-llama \
|
||||||
|
-mw ./models/ggml-small.en.bin \
|
||||||
|
-ml ../llama.cpp/models/13B/ggml-model-q4_0.bin \
|
||||||
|
--name-ni "Georgi" \
|
||||||
|
--name-ai "SSaMA" \
|
||||||
|
-t 8 -vid 2 --speak ./examples/talk-llama/speak.sh
|
138
whisper.cpp
138
whisper.cpp
@ -1,8 +1,5 @@
|
|||||||
#define WHISPER_BUILD
|
#define WHISPER_BUILD
|
||||||
#include "whisper.h"
|
#include "whisper.h"
|
||||||
#if WHISPER_USE_COREML
|
|
||||||
#include "coreml/whisper-encoder.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ggml.h"
|
#include "ggml.h"
|
||||||
|
|
||||||
@ -221,14 +218,14 @@ static const std::map<std::string, std::pair<int, std::string>> g_lang = {
|
|||||||
{ "su", { 98, "sundanese", } },
|
{ "su", { 98, "sundanese", } },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const size_t MB = 1024*1024;
|
static const size_t MB = 1ull*1024*1024;
|
||||||
|
|
||||||
static const std::map<e_model, size_t> MEM_REQ_SCRATCH0 = {
|
static const std::map<e_model, size_t> MEM_REQ_SCRATCH0 = {
|
||||||
{ MODEL_TINY, 12ull*MB },
|
{ MODEL_TINY, 14ull*MB },
|
||||||
{ MODEL_BASE, 15ull*MB },
|
{ MODEL_BASE, 18ull*MB },
|
||||||
{ MODEL_SMALL, 23ull*MB },
|
{ MODEL_SMALL, 28ull*MB },
|
||||||
{ MODEL_MEDIUM, 31ull*MB },
|
{ MODEL_MEDIUM, 36ull*MB },
|
||||||
{ MODEL_LARGE, 38ull*MB },
|
{ MODEL_LARGE, 44ull*MB },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::map<e_model, size_t> MEM_REQ_SCRATCH1 = {
|
static const std::map<e_model, size_t> MEM_REQ_SCRATCH1 = {
|
||||||
@ -589,10 +586,6 @@ struct whisper_state {
|
|||||||
|
|
||||||
int lang_id = 0; // english by default
|
int lang_id = 0; // english by default
|
||||||
|
|
||||||
#ifdef WHISPER_USE_COREML
|
|
||||||
whisper_coreml_context * ctx_coreml;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// [EXPERIMENTAL] token-level timestamps data
|
// [EXPERIMENTAL] token-level timestamps data
|
||||||
int64_t t_beg = 0;
|
int64_t t_beg = 0;
|
||||||
int64_t t_last = 0;
|
int64_t t_last = 0;
|
||||||
@ -661,9 +654,11 @@ static bool kv_cache_init(
|
|||||||
int n_ctx) {
|
int n_ctx) {
|
||||||
cache.buf.resize(mem_bytes);
|
cache.buf.resize(mem_bytes);
|
||||||
|
|
||||||
struct ggml_init_params params;
|
struct ggml_init_params params = {
|
||||||
params.mem_size = cache.buf.size();
|
/*.mem_size =*/ cache.buf.size(),
|
||||||
params.mem_buffer = cache.buf.data();
|
/*.mem_buffer =*/ cache.buf.data(),
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
|
};
|
||||||
|
|
||||||
cache.ctx = ggml_init(params);
|
cache.ctx = ggml_init(params);
|
||||||
|
|
||||||
@ -695,9 +690,11 @@ static bool kv_cache_reinit(struct whisper_kv_cache & cache) {
|
|||||||
|
|
||||||
WHISPER_ASSERT(cache.buf.size() >= 2*n_elements*ggml_type_size(wtype));
|
WHISPER_ASSERT(cache.buf.size() >= 2*n_elements*ggml_type_size(wtype));
|
||||||
|
|
||||||
struct ggml_init_params params;
|
struct ggml_init_params params = {
|
||||||
params.mem_size = cache.buf.size();
|
/*.mem_size =*/ cache.buf.size(),
|
||||||
params.mem_buffer = cache.buf.data();
|
/*.mem_buffer =*/ cache.buf.data(),
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
|
};
|
||||||
|
|
||||||
cache.ctx = ggml_init(params);
|
cache.ctx = ggml_init(params);
|
||||||
|
|
||||||
@ -1035,9 +1032,11 @@ static bool whisper_model_load(struct whisper_model_loader * loader, whisper_con
|
|||||||
|
|
||||||
// create the ggml context
|
// create the ggml context
|
||||||
{
|
{
|
||||||
struct ggml_init_params params;
|
struct ggml_init_params params = {
|
||||||
params.mem_size = wctx.model.buf->size();
|
/*.mem_size =*/ wctx.model.buf->size(),
|
||||||
params.mem_buffer = wctx.model.buf->data();
|
/*.mem_buffer =*/ wctx.model.buf->data(),
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
|
};
|
||||||
|
|
||||||
model.ctx = ggml_init(params);
|
model.ctx = ggml_init(params);
|
||||||
if (!model.ctx) {
|
if (!model.ctx) {
|
||||||
@ -1351,9 +1350,11 @@ static bool whisper_encode_internal(
|
|||||||
const int n_mels = hparams.n_mels;
|
const int n_mels = hparams.n_mels;
|
||||||
assert(mel_inp.n_mel == n_mels);
|
assert(mel_inp.n_mel == n_mels);
|
||||||
|
|
||||||
struct ggml_init_params params;
|
struct ggml_init_params params = {
|
||||||
params.mem_size = wstate.buf_compute.size();
|
/*.mem_size =*/ wstate.buf_compute.size(),
|
||||||
params.mem_buffer = wstate.buf_compute.data();
|
/*.mem_buffer =*/ wstate.buf_compute.data(),
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
|
};
|
||||||
|
|
||||||
struct ggml_context * ctx0 = ggml_init(params);
|
struct ggml_context * ctx0 = ggml_init(params);
|
||||||
|
|
||||||
@ -1375,7 +1376,6 @@ static bool whisper_encode_internal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef WHISPER_USE_COREML
|
|
||||||
struct ggml_tensor * cur;
|
struct ggml_tensor * cur;
|
||||||
|
|
||||||
// convolution + gelu
|
// convolution + gelu
|
||||||
@ -1684,13 +1684,6 @@ static bool whisper_encode_internal(
|
|||||||
|
|
||||||
//ggml_graph_print(&gf);
|
//ggml_graph_print(&gf);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
wstate.use_buf(ctx0, -1);
|
|
||||||
|
|
||||||
struct ggml_tensor * cur = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_state, n_ctx);
|
|
||||||
|
|
||||||
whisper_coreml_encode(wstate.ctx_coreml, (float *) mel->data, (float *) cur->data);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// cur
|
// cur
|
||||||
//{
|
//{
|
||||||
@ -1758,10 +1751,10 @@ static bool whisper_encode_internal(
|
|||||||
|
|
||||||
//printf("%s: used_mem = %f MB, %f MB, %f MB %f MB %f MB\n", __func__,
|
//printf("%s: used_mem = %f MB, %f MB, %f MB %f MB %f MB\n", __func__,
|
||||||
// ggml_used_mem(ctx0)/1024.0/1024.0,
|
// ggml_used_mem(ctx0)/1024.0/1024.0,
|
||||||
// wctx.get_buf_max_mem(0)/1024.0/1024.0,
|
// wstate.get_buf_max_mem(0)/1024.0/1024.0,
|
||||||
// wctx.get_buf_max_mem(1)/1024.0/1024.0,
|
// wstate.get_buf_max_mem(1)/1024.0/1024.0,
|
||||||
// wctx.get_buf_max_mem(2)/1024.0/1024.0,
|
// wstate.get_buf_max_mem(2)/1024.0/1024.0,
|
||||||
// wctx.get_buf_max_mem(3)/1024.0/1024.0);
|
// wstate.get_buf_max_mem(3)/1024.0/1024.0);
|
||||||
|
|
||||||
ggml_free(ctx0);
|
ggml_free(ctx0);
|
||||||
|
|
||||||
@ -1812,9 +1805,11 @@ static bool whisper_decode_internal(
|
|||||||
|
|
||||||
//WHISPER_PRINT_DEBUG("%s: n_past = %d, N = %d, M = %d, n_ctx = %d\n", __func__, n_past, N, M, n_ctx);
|
//WHISPER_PRINT_DEBUG("%s: n_past = %d, N = %d, M = %d, n_ctx = %d\n", __func__, n_past, N, M, n_ctx);
|
||||||
|
|
||||||
struct ggml_init_params params;
|
struct ggml_init_params params = {
|
||||||
params.mem_size = wstate.buf_compute.size();
|
/*.mem_size =*/ wstate.buf_compute.size(),
|
||||||
params.mem_buffer = wstate.buf_compute.data();
|
/*.mem_buffer =*/ wstate.buf_compute.data(),
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
|
};
|
||||||
|
|
||||||
struct ggml_context * ctx0 = ggml_init(params);
|
struct ggml_context * ctx0 = ggml_init(params);
|
||||||
|
|
||||||
@ -2168,10 +2163,10 @@ static bool whisper_decode_internal(
|
|||||||
if (N > 1) {
|
if (N > 1) {
|
||||||
//printf("%s: used_mem = %f MB, %f MB, %f MB %f MB %f MB\n", __func__,
|
//printf("%s: used_mem = %f MB, %f MB, %f MB %f MB %f MB\n", __func__,
|
||||||
// ggml_used_mem(ctx0)/1024.0/1024.0,
|
// ggml_used_mem(ctx0)/1024.0/1024.0,
|
||||||
// wctx.get_buf_max_mem(0)/1024.0/1024.0,
|
// wstate.get_buf_max_mem(0)/1024.0/1024.0,
|
||||||
// wctx.get_buf_max_mem(1)/1024.0/1024.0,
|
// wstate.get_buf_max_mem(1)/1024.0/1024.0,
|
||||||
// wctx.get_buf_max_mem(2)/1024.0/1024.0,
|
// wstate.get_buf_max_mem(2)/1024.0/1024.0,
|
||||||
// wctx.get_buf_max_mem(3)/1024.0/1024.0);
|
// wstate.get_buf_max_mem(3)/1024.0/1024.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_free(ctx0);
|
ggml_free(ctx0);
|
||||||
@ -2475,20 +2470,6 @@ static std::vector<whisper_vocab::id> tokenize(const whisper_vocab & vocab, cons
|
|||||||
// interface implementation
|
// interface implementation
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifdef WHISPER_USE_COREML
|
|
||||||
// replace .bin with .mlmodelc
|
|
||||||
static std::string whisper_get_coreml_path(std::string path_bin) {
|
|
||||||
auto pos = path_bin.rfind('.');
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
path_bin = path_bin.substr(0, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
path_bin += ".mlmodelc";
|
|
||||||
|
|
||||||
return path_bin;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct whisper_state * whisper_init_state(whisper_context * ctx) {
|
struct whisper_state * whisper_init_state(whisper_context * ctx) {
|
||||||
whisper_state * state = new whisper_state;
|
whisper_state * state = new whisper_state;
|
||||||
|
|
||||||
@ -2533,21 +2514,6 @@ struct whisper_state * whisper_init_state(whisper_context * ctx) {
|
|||||||
|
|
||||||
state->rng = std::mt19937(0);
|
state->rng = std::mt19937(0);
|
||||||
|
|
||||||
#ifdef WHISPER_USE_COREML
|
|
||||||
const auto path_coreml = whisper_get_coreml_path(ctx->path_model);
|
|
||||||
|
|
||||||
fprintf(stderr, "%s: loading Core ML model from '%s'\n", __func__, path_coreml.c_str());
|
|
||||||
fprintf(stderr, "%s: first run on a device may take a while ...\n", __func__);
|
|
||||||
|
|
||||||
state->ctx_coreml = whisper_coreml_init(path_coreml.c_str());
|
|
||||||
if (!state->ctx_coreml) {
|
|
||||||
fprintf(stderr, "%s: failed to load Core ML model from '%s'\n", __func__, path_coreml.c_str());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "%s: Core ML model loaded\n", __func__);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2563,7 +2529,6 @@ struct whisper_context * whisper_init_from_file_no_state(const char * path_model
|
|||||||
}
|
}
|
||||||
|
|
||||||
loader.context = &fin;
|
loader.context = &fin;
|
||||||
|
|
||||||
loader.read = [](void * ctx, void * output, size_t read_size) {
|
loader.read = [](void * ctx, void * output, size_t read_size) {
|
||||||
std::ifstream * fin = (std::ifstream*)ctx;
|
std::ifstream * fin = (std::ifstream*)ctx;
|
||||||
fin->read((char *)output, read_size);
|
fin->read((char *)output, read_size);
|
||||||
@ -2711,10 +2676,6 @@ void whisper_free(struct whisper_context * ctx) {
|
|||||||
|
|
||||||
whisper_free_state(ctx->state);
|
whisper_free_state(ctx->state);
|
||||||
|
|
||||||
#ifdef WHISPER_USE_COREML
|
|
||||||
whisper_coreml_free(ctx->state->ctx_coreml);
|
|
||||||
ctx->state->ctx_coreml = nullptr;
|
|
||||||
#endif
|
|
||||||
delete ctx;
|
delete ctx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3170,6 +3131,7 @@ struct whisper_full_params whisper_full_default_params(enum whisper_sampling_str
|
|||||||
/*.speed_up =*/ false,
|
/*.speed_up =*/ false,
|
||||||
/*.audio_ctx =*/ 0,
|
/*.audio_ctx =*/ 0,
|
||||||
|
|
||||||
|
/*.initial_prompt =*/ nullptr,
|
||||||
/*.prompt_tokens =*/ nullptr,
|
/*.prompt_tokens =*/ nullptr,
|
||||||
/*.prompt_n_tokens =*/ 0,
|
/*.prompt_n_tokens =*/ 0,
|
||||||
|
|
||||||
@ -3200,6 +3162,9 @@ struct whisper_full_params whisper_full_default_params(enum whisper_sampling_str
|
|||||||
/*.new_segment_callback =*/ nullptr,
|
/*.new_segment_callback =*/ nullptr,
|
||||||
/*.new_segment_callback_user_data =*/ nullptr,
|
/*.new_segment_callback_user_data =*/ nullptr,
|
||||||
|
|
||||||
|
/*.progress_callback =*/ nullptr,
|
||||||
|
/*.progress_callback_user_data =*/ nullptr,
|
||||||
|
|
||||||
/*.encoder_begin_callback =*/ nullptr,
|
/*.encoder_begin_callback =*/ nullptr,
|
||||||
/*.encoder_begin_callback_user_data =*/ nullptr,
|
/*.encoder_begin_callback_user_data =*/ nullptr,
|
||||||
|
|
||||||
@ -3842,6 +3807,15 @@ int whisper_full_with_state(
|
|||||||
prompt_past.clear();
|
prompt_past.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initial prompt
|
||||||
|
if (!params.prompt_tokens && params.initial_prompt) {
|
||||||
|
std::vector<whisper_token> prompt_tokens;
|
||||||
|
prompt_tokens.resize(1024);
|
||||||
|
prompt_tokens.resize(whisper_tokenize(ctx, params.initial_prompt, prompt_tokens.data(), prompt_tokens.size()));
|
||||||
|
params.prompt_tokens = prompt_tokens.data();
|
||||||
|
params.prompt_n_tokens = prompt_tokens.size();
|
||||||
|
}
|
||||||
|
|
||||||
// prepend the prompt tokens to the prompt_past
|
// prepend the prompt tokens to the prompt_past
|
||||||
if (params.prompt_tokens && params.prompt_n_tokens > 0) {
|
if (params.prompt_tokens && params.prompt_n_tokens > 0) {
|
||||||
// parse tokens from the pointer
|
// parse tokens from the pointer
|
||||||
@ -3907,6 +3881,10 @@ int whisper_full_with_state(
|
|||||||
fprintf(stderr, "%s: progress = %3d%%\n", __func__, progress_prev);
|
fprintf(stderr, "%s: progress = %3d%%\n", __func__, progress_prev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (params.progress_callback) {
|
||||||
|
params.progress_callback(
|
||||||
|
ctx, ctx->state, progress_prev, params.progress_callback_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
// of only 1 second left, then stop
|
// of only 1 second left, then stop
|
||||||
if (seek + 100 >= seek_end) {
|
if (seek + 100 >= seek_end) {
|
||||||
@ -4495,6 +4473,9 @@ int whisper_full_parallel(
|
|||||||
params_cur.new_segment_callback = nullptr;
|
params_cur.new_segment_callback = nullptr;
|
||||||
params_cur.new_segment_callback_user_data = nullptr;
|
params_cur.new_segment_callback_user_data = nullptr;
|
||||||
|
|
||||||
|
params_cur.progress_callback = nullptr;
|
||||||
|
params_cur.progress_callback_user_data = nullptr;
|
||||||
|
|
||||||
workers[i] = std::thread(whisper_full_with_state, ctx, states[i], std::move(params_cur), samples + start_samples, n_samples_cur);
|
workers[i] = std::thread(whisper_full_with_state, ctx, states[i], std::move(params_cur), samples + start_samples, n_samples_cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4755,6 +4736,7 @@ WHISPER_API const char * whisper_bench_ggml_mul_mat_str(int n_threads) {
|
|||||||
struct ggml_init_params gparams = {
|
struct ggml_init_params gparams = {
|
||||||
/*.mem_size =*/ buf.size(),
|
/*.mem_size =*/ buf.size(),
|
||||||
/*.mem_buffer =*/ buf.data(),
|
/*.mem_buffer =*/ buf.data(),
|
||||||
|
/*.no_alloc =*/ false,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ggml_context * ctx0 = ggml_init(gparams);
|
struct ggml_context * ctx0 = ggml_init(gparams);
|
||||||
|
@ -306,6 +306,9 @@ extern "C" {
|
|||||||
// Use the whisper_full_...() functions to obtain the text segments
|
// Use the whisper_full_...() functions to obtain the text segments
|
||||||
typedef void (*whisper_new_segment_callback)(struct whisper_context * ctx, struct whisper_state * state, int n_new, void * user_data);
|
typedef void (*whisper_new_segment_callback)(struct whisper_context * ctx, struct whisper_state * state, int n_new, void * user_data);
|
||||||
|
|
||||||
|
// Progress callback
|
||||||
|
typedef void (*whisper_progress_callback)(struct whisper_context * ctx, struct whisper_state * state, int progress, void * user_data);
|
||||||
|
|
||||||
// Encoder begin callback
|
// Encoder begin callback
|
||||||
// If not NULL, called before the encoder starts
|
// If not NULL, called before the encoder starts
|
||||||
// If it returns false, the computation is aborted
|
// If it returns false, the computation is aborted
|
||||||
@ -356,6 +359,7 @@ extern "C" {
|
|||||||
|
|
||||||
// tokens to provide to the whisper decoder as initial prompt
|
// tokens to provide to the whisper decoder as initial prompt
|
||||||
// these are prepended to any existing text context from a previous call
|
// these are prepended to any existing text context from a previous call
|
||||||
|
const char * initial_prompt;
|
||||||
const whisper_token * prompt_tokens;
|
const whisper_token * prompt_tokens;
|
||||||
int prompt_n_tokens;
|
int prompt_n_tokens;
|
||||||
|
|
||||||
@ -391,6 +395,10 @@ extern "C" {
|
|||||||
whisper_new_segment_callback new_segment_callback;
|
whisper_new_segment_callback new_segment_callback;
|
||||||
void * new_segment_callback_user_data;
|
void * new_segment_callback_user_data;
|
||||||
|
|
||||||
|
// called on each progress update
|
||||||
|
whisper_progress_callback progress_callback;
|
||||||
|
void * progress_callback_user_data;
|
||||||
|
|
||||||
// called each time before the encoder starts
|
// called each time before the encoder starts
|
||||||
whisper_encoder_begin_callback encoder_begin_callback;
|
whisper_encoder_begin_callback encoder_begin_callback;
|
||||||
void * encoder_begin_callback_user_data;
|
void * encoder_begin_callback_user_data;
|
||||||
|
Reference in New Issue
Block a user