Языковые модели для улучшения рабочего процесса научного моделирования

CoderStudio, 20.03.2023 12:37
Языковые модели для улучшения рабочего процесса научного моделирования

Процесс разработки научного рабочего процесса часто содержит следующий основной контур:

  1. Разработка и проверка параметризованной модели.
  2. Разработка сценариев постобработки для визуализации.
  3. Поиск возможностей ускорения и улучшения.
  4. Провести исследование параметров.

В этой статье показаны впечатляющие способы, которыми ChatGPT (или другие языковые модели) может помочь, помимо выполнения самой реализации. В статье также показаны некоторые ограничения и подчеркивается важность двойной проверки того, что вы делаете, и не стоит полностью полагаться на этот мощный инструмент.

Визуализация и построение графиков

Хотя научные графики кажутся простыми, они часто требуют часов времени для создания простых, описательных, чистых графиков. Chat-GPT может помочь, заранее подготовив для вас много шаблонов, что позволит вам сосредоточиться на самом содержании.

Некоторые примеры:

  1. Задание: Создайте тепловую карту на основе трехмерных данных с помощью seaborn

Вывод: некоторая описательная преамбула, за которой следует текстовое описание всех важных переменных

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# Create some random 3D data
data = pd.DataFrame({'x': np.random.rand(100),
                     'y': np.random.rand(100),
                     'z': np.random.rand(100)})

# Use pivot_table to create a 2D table
pivot_data = data.pivot_table(index='y', columns='x', values='z')

# Create heatmap using Seaborn
sns.heatmap(pivot_data, cmap='coolwarm')

# Show the plot
plt.show()

2. Подсказка: Используйте plotly для создания интерактивной гистограммы, раскрашенной по третьему параметру. Данные хранятся в датафрейме. После этого сохраните на диск.

Выходные данные:

import plotly.express as px
import pandas as pd

# Create some example data
df = pd.DataFrame({'category': ['A', 'A', 'B', 'B', 'C', 'C'],
                   'value1': [20, 30, 40, 50, 60, 70],
                   'value2': [10, 20, 30, 40, 50, 60],
                   'color': ['blue', 'red', 'blue', 'red', 'blue', 'red']})

# Create stacked bar chart
fig = px.bar(df, x='category', y=['value1', 'value2'], color='color')

# Show the plot
fig.show()

# Save to disk
fig.write_html('stacked_bar_chart.html')

3. Задание: Создайте 3 линейных графика и диаграмму рассеяния на одной диаграмме в matplotlib. Раскрасьте линии в соответствии с их типом. Добавьте аннотационные поля и стрелки для выделения пиков.

Выходные данные:

import matplotlib.pyplot as plt
import numpy as np

# Create some example data
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.tan(x)
types = np.random.choice(['Type A', 'Type B', 'Type C'], size=100)

# Create the figure and axis objects
fig, ax = plt.subplots()

# Plot the line charts
ax.plot(x, y1, label='Type A')
ax.plot(x, y2, label='Type B')
ax.plot(x, y3, label='Type C')

# Plot the scatter plot
colors = {'Type A': 'r', 'Type B': 'g', 'Type C': 'b'}
ax.scatter(x, y1 + y2 + y3, c=[colors[t] for t in types], label='Sum of values')

# Add annotations
for i in [10, 60, 90]:
    ax.annotate(f"Peak {i}", xy=(x[i], y1[i] + y2[i] + y3[i]), xytext=(x[i] + 1, y1[i] + y2[i] + y3[i] + 1),
                arrowprops=dict(facecolor='black', shrink=0.05))

# Add legend
ax.legend()

# Show the plot
plt.show()
Выходные данные

Полученные результаты могут быть не идеальными, но создают зачатки сложного графика. Это дает хорошую основу и позволяет сосредоточиться на совершенствовании графиков, а не на ошибках matplotlib.

4. Использование TikZ для рисования векторных графических изображений

Одним из особенно полезных, но потрясающе утомительных инструментов является TikZ, который позволяет кодировать изображения векторной графики в LaTeX. ChatGPT - это фантастический инструмент, который может помочь создать шаблон для более сложного изображения.

Ввод: С помощью tikz нарисуйте 2d сетку со снеговиком в центре. Снеговик имеет черные контуры и морковный нос.

Выходные данные:

\documentclass{article}
\usepackage{tikz}

\begin{document}

\begin{tikzpicture}[scale=0.5]

% Draw the grid
\foreach \x in {0,1,...,7} {
  \foreach \y in {0,1,...,7} {
    \draw (\x,\y) rectangle (\x+1,\y+1);
  }
}

% Draw the snowman
\fill[color=white,draw=black] (3,3) circle (2cm);
\fill[color=white,draw=black] (3,6) circle (1.5cm);
\fill[color=white,draw=black] (3,7.5) circle (1cm);
\fill[color=black] (2.5,7.5) circle (0.15cm);
\fill[color=black] (3.5,7.5) circle (0.15cm);
\fill[color=black] (3,6) circle (0.15cm);
\draw[black] (2.5,6) -- (4,6);
\draw[black] (2,5.5) -- (2,4.5);
\draw[black] (4,5.5) -- (4,4.5);
\draw[black] (2,4.5) arc (180:360:0.5);
\draw[black] (4,4.5) arc (0:180:0.5);
\fill[color=orange!70] (3,7) -- (3.5,7) -- (3.25,6.75) -- cycle;

\end{tikzpicture}

\end{document}

Соответствующее изображение

Соответствующее изображение

...называть его причудливым - великодушно... но он обеспечивает большинство основ, необходимых для совершенствования образа.

Ускорение и распараллеливание

  1. Основные улучшения с помощью векторизации, встроенной в numpy

Исходные данные: Оптимизируйте следующий код

lst = np.arange(1000)
for i in range(len(lst)):
   lst[i] = lst[i] + 3

Вывод: Следующий код может быть оптимизирован с помощью векторизации numpy вместо использования цикла for для перебора каждого элемента массива numpy:

lst = np.arange(1000)
lst += 3

2. Использование многопоточности для ускорения циклов for

Исходные данные: Использование многопоточности для ускорения следующего цикла

lst = np.arange(1000)
acc = 0
for i in range(len(lst)):
   acc += lst[i]

Выходные данные:

import numpy as np
import concurrent.futures

def sum_worker(lst, start, end):
    return np.sum(lst[start:end])

lst = np.arange(1000)
chunk_size = 100
num_chunks = len(lst) // chunk_size + 1

with concurrent.futures.ThreadPoolExecutor() as executor:
    futures = []
    for i in range(num_chunks):
        start = i * chunk_size
        end = min(start + chunk_size, len(lst))
        futures.append(executor.submit(sum_worker, lst, start, end))

    acc = sum([f.result() for f in futures])

3. Переход на язык программирования более низкого уровня

Преимуществами языков программирования высокого уровня часто являются доступность и простота обслуживания и разработки. Однако такое поведение во время выполнения часто обходится в копеечку.

Исходные данные: Написать код python на C++

lst = np.arange(100000)
acc = 0
for i in range(len(lst)):
   acc += lst[i]

Выходные данные:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> lst(100000);
    int acc = 0;

    for (int i = 0; i < lst.size(); i++) {
        acc += lst[i];
    }

    std::cout << acc << std::endl;

    return 0;
}

4. Затем мы можем преобразовать код на C++ в многоядерный код с поддержкой параллельных вычислений с помощью библиотеки MPI.

Вход: Переписать код на C++ для MPI

Выходные данные:

#include <iostream>
#include <vector>
#include "mpi.h"

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);

    int rank, size;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    std::vector<int> lst(100000);
    int local_sum = 0;

    // Divide work among processes
    int chunk_size = lst.size() / size;
    int remainder = lst.size() % size;
    int start_idx = rank * chunk_size;
    int end_idx = start_idx + chunk_size + (rank == size - 1 ? remainder : 0);

    // Compute local sum
    for (int i = start_idx; i < end_idx; i++) {
        local_sum += lst[i];
    }

    // Reduce results
    int global_sum;
    MPI_Reduce(&local_sum, &global_sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);

    // Output result from process 0
    if (rank == 0) {
        std::cout << global_sum << std::endl;
    }

    MPI_Finalize();
    return 0;
}

То же самое можно сделать для OpenMP.

5. Наконец, мы можем использовать ChatGPT для переноса того же кода на CUDA, чтобы он был способен работать на GPU.

Вход: Перенести этот код в CUDA

Выходные данные:

#include <iostream>
#include <vector>
#include <cuda_runtime.h>

__global__ void sum_kernel(int* lst, int n, int* result) {
    int tid = threadIdx.x + blockIdx.x * blockDim.x;
    int stride = blockDim.x * gridDim.x;

    int sum = 0;
    for (int i = tid; i < n; i += stride) {
        sum += lst[i];
    }

    atomicAdd(result, sum);
}

int main() {
    const int N = 100000;
    std::vector<int> lst(N);
    int acc = 0;

    // Initialize device memory
    int* d_lst, *d_acc;
    cudaMalloc(&d_lst, N * sizeof(int));
    cudaMalloc(&d_acc, sizeof(int));
    cudaMemcpy(d_lst, lst.data(), N * sizeof(int), cudaMemcpyHostToDevice);
    cudaMemset(d_acc, 0, sizeof(int));

    // Run kernel
    int threads_per_block = 256;
    int blocks_per_grid = (N + threads_per_block - 1) / threads_per_block;
    sum_kernel<<<blocks_per_grid, threads_per_block>>>(d_lst, N, d_acc);

    // Copy result back to host
    cudaMemcpy(&acc, d_acc, sizeof(int), cudaMemcpyDeviceToHost);

    // Clean up device memory
    cudaFree(d_lst);
    cudaFree(d_acc);

    std::cout << acc << std::endl;

    return 0;
}

Настроить исследование параметров

Вход: Создайте bash-скрипт для pbs, чтобы запустить исследование параметров путем параллельной подачи 10 заданий, изменяя X от 0,1 до 1 с шагом 0,1.

Выходные данные:

#!/bin/bash

#PBS -N parameter_study
#PBS -l nodes=1:ppn=10
#PBS -l walltime=00:30:00
#PBS -j oe

# Set the range of X values
X_START=0.1
X_END=1.0
X_STEP=0.1

# Loop over the X values and submit jobs in parallel
for X in $(seq ${X_START} ${X_STEP} ${X_END}); do
    # Set the name for this job
    JOB_NAME="parameter_study_X=${X}"

    # Write the PBS script for this job
    cat > ${JOB_NAME}.pbs << EOF
#!/bin/bash
#PBS -N ${JOB_NAME}
#PBS -l nodes=1:ppn=1
#PBS -l walltime=00:10:00
#PBS -j oe
cd $PBS_O_WORKDIR
python my_script.py --param1=${X}
EOF

    # Submit the job
    qsub ${JOB_NAME}.pbs
done

Запустив этот сценарий bash, вы сможете настроить исследование параметров. Правда, для этого потребовалось еще несколько попыток настройки. Предыдущие попытки давали код, который не запускался.

Резюме

Chat-GPT - хороший инструмент. Однако, без определенного опыта и умения читать и писать код, он может легко сбить вас с пути и заставить потратить еще больше времени на отладку того, что он сгенерировал, вместо того, чтобы начинать с нуля с самого начала.

Некоторые рекомендации

  • Всегда тестируйте код.
  • Бросьте сгенерированный код обратно в ChatGPT и попросите его отладить код. Часто он может найти ошибки и очистить код со второго захода.
  • Не полагайтесь на него, если вы не знаете, с чего начать. Тем не менее, он отлично справляется с генерацией шаблонного кода, в который вам предстоит вписать детали.
  • Учитесь на сгенерированном коде! Это, возможно, самый простой способ познакомиться с новыми функциями и возможностями без необходимости читать бесчисленные блоги и документацию.