Realizando Inferências em CPU na Power10
Contexto
Neste post iremos apresentar a nossa experiência em executar o modelo Granite-20b-Code-Instruct em uma máquina Power10, apresentando os desafios e demais configurações necessárias para realizar inferências utilizando o Llama.cpp, uma das bibliotecas opensource mais populares neste domínio.
TLDR
- Este post apresenta detalhes sobre como configurar e realizar inferências utilizando a infraestrutura da IBM Power 10;
- Nosso maior desafio foi a configuração do Llama cpp, que demandou ajustes como a instalação do Ninja-builder, realização da compilação do OpenBLAS e atualização do compilador C.
Infraestrutura
As inferências foram realizadas em uma máquina com arquitetura IBM POWER10, equipada com 750GB de memória RAM e executando o sistema operacional Red Hat Enterprise Linux 8.10. O acesso ao ambiente é realizado por meio de uma VM, sendo necessário o uso de uma VPN para estabelecer uma comunicação segura e controlada com o sistema, possibilitando a execução das atividades de forma remota e eficiente.
Setup Inicial
A biblioteca que nos permite executar LLMs utilizando os recursos computacionais da CPU é o Llama.cpp. Para a sua configuração, foi necessário resolver duas dependências externas: o Ninja-builder e o OpenBLAS. O NinjaBuilder é responsável por otimizar o processo de compilação, enquanto o OpenBLAS é uma biblioteca responsável pelos cálculos matriciais de alto desempenho.
Durante o processo de build do OpenBLAS, identificamos discrepâncias nos testes internos de validação dos cálculos matriciais, indicando um problema de compatibilidade com o compilador C disponível, que estava em uma versão mais antiga, a 8.5.0. A solução, portanto, foi a atualização do compilador para uma versão mais recente, a 13.2, garantindo melhor compatibilidade com a arquitetura Power10 e validando a precisão das operações numéricas necessárias para o funcionamento do Llama.cpp. A seguir, apresentamos o passo a passo realizado para viabilizar a compilação das bibliotecas necessárias, bem como a atualização do compilador C.
- Criando o ambiente de compilação para o builder
sudo dnf update -y && dnf -y groupinstall 'Development Tools' && dnf install -y \ cmake git ninja-build-debugsource.ppc64le \ && dnf clean all
- Atualizando compilador C e definindo variáveis de ambiente
scl enable gcc-toolset-13 bash
export CC=/usr/bin/gcc-13
export CXX=/usr/bin/g++-13
- Baixando e compilando o OpenBLAS
git clone --recursive https://github.com/DanielCasali/OpenBLAS.git && cd OpenBLAS && \ make -j$(nproc --all) TARGET=POWER10 DYNAMIC_ARCH=1 && \ make PREFIX=/opt/OpenBLAS install && \ cd /
- Baixando e compilando o Llama.cpp usando a biblioteca OpenBLAS que acabamos de baixar
git clone https://github.com/DanielCasali/llama.cpp.git && cd llama.cpp && sed -i "s/powerpc64le/native -mvsx -mtune=native -D__POWER10_VECTOR__/g" ggml/src/CMakeLists.txt && \ mkdir build; \ cd build; \ cmake -DGGML_BLAS=ON -DGGML_BLAS_VENDOR=OpenBLAS -DBLAS_INCLUDE_DIRS=/opt/OpenBLAS/include -G Ninja ..; \ cmake --build . --config Release
Com todos esses passos realizados com sucesso, o ambiente foi devidamente configurado e otimizado para a execução local do Llama.cpp. Agora, somos capazes de iniciar um servidor para realizar inferências com LLM’s de forma eficiente, utilizando exclusivamente os recursos da CPU.
Realizando Inferência
Nós escolhemos o modelo Granite-20b-code-instruct no formato .GGUF, que é desenvolvido especificamente para otimizar o desempenho de modelos de linguagem em ambientes que utilizam apenas CPU. Esses modelos são quantizados, ou seja, a precisão dos cálculos feitos por eles são reduzidas, e, por conseguinte, o tamanho e consumo de memória também são menores, tornando-os ideais para a execução eficiente com Llama.cpp. Essa abordagem viabiliza inferências locais com alto desempenho, mesmo em arquiteturas baseadas exclusivamente em processadores, como é o caso da POWER10. O download do modelo foi feito diretamente do Hugging Face. A seguir, mostraremos o passo a passo para realizar o download:
- Criar um diretório para o modelo no Llama.cpp:
mkdir -p /root/llama.cpp/models/granite-20b-code-instruct-8k-GGUF
- Acessar o diretório no Llama.cpp:
cd /root/llama.cpp/models/granite-20b-code-instruct-8k-GGUF
- Baixar o modelo via Hugging Face:
wget https://huggingface.co/ibm-granite/granite-20b-code-instruct-8k-GGUF/resolve/main/granite-20b-code-instruct.Q4_K_M.gguf
O último passo pode ser mais demorado a depender da quantidade de parâmetros do modelo. Todavia, após concluir os passos acima, podemos subir um servidor Llama.cpp para que seja possível realizarmos inferências, por padrão, o servidor é exposto na porta 8080 da Power10, mas isso é completamente customizável. O código a seguir ilustra como configurar e executar o servidor Llama:
/root/llama.cpp/build/bin/llama-server --host 0.0.0.0 --model /root/llama.cpp/models/granite-20b-code-instruct-8k-GGUF/granite-20b-code-instruct.Q4_K_M.gguf
Com o servidor do Llama.cpp executando na porta 8080, agora somos capazes de realizar inferências via requisições HTTP. Neste exemplo, para fins de simplicidade, utilizamos o curl para requisições:
curl -X POST http://localhost:8080/completion \
-H "Content-Type: application/json" \
-d '{
"prompt": "Make a hello world program in Java. Your answer should be in Java code only.",
"max_tokens": 100
}'
A seguir, um exemplo de como a resposta é retornada:
{
"content": "
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Com isso, agora somos capazes de realizar inferências em CPU. Nossos próximos passos visa realizar essas inferências utilizando o Framework de avaliação HELM (Holistic Evaluation of Language Models) como mediador.