Low-Code Development: Leverage low and no code to streamline your workflow so that you can focus on higher priorities.
DZone Security Research: Tell us your top security strategies in 2024, influence our research, and enter for a chance to win $!
Artificial intelligence (AI) and machine learning (ML) are two fields that work together to create computer systems capable of perception, recognition, decision-making, and translation. Separately, AI is the ability for a computer system to mimic human intelligence through math and logic, and ML builds off AI by developing methods that "learn" through experience and do not require instruction. In the AI/ML Zone, you'll find resources ranging from tutorials to use cases that will help you navigate this rapidly growing field.
10 ChatGPT Prompts To Boost Developer Productivity
The Impact of AI and Platform Engineering on Cloud Native's Evolution: Automate Your Cloud Journey to Light Speed
In this article, we’ll explore how to build intelligent AI agents using Azure Open AI and semantic kernels (Microsoft C# SDK). You can combine it with Open AI, Azure Open AI, Hugging Face, or any other model. We’ll cover the fundamentals, dive into implementation details, and provide practical code examples in C#. Whether you’re a beginner or an experienced developer, this guide will help you harness the power of AI for your applications. What Is Semantic Kernel? In Kevin Scott's talk on "The era of the AI copilot," he showcased how Microsoft's Copilot system uses a mix of AI models and plugins to enhance user experiences. At the core of this setup is an AI orchestration layer, which allows Microsoft to combine these AI components to create innovative features for users. For developers looking to create their own copilot-like experiences using AI plugins, Microsoft has introduced Semantic kernel. Semantic Kernel is an open-source framework that enables developers to build intelligent agents by providing a common interface for various AI models and algorithms. The Semantic Kernel SDK allows you to integrate the power of large language models (LLMs) in your own applications. The Semantic Kernel SDK allows developers to integrate prompts to LLMs and results in their applications, and potentially craft their own copilot-like experiences. It allows developers to focus on building intelligent applications without worrying about the underlying complexities of AI models. Semantic Kernel is built on top of the .NET ecosystem and provides a robust and scalable platform for building intelligent apps/agents. Figure courtesy of Microsoft Key Features of Semantic Kernel Modular architecture: Semantic Kernel has a modular architecture that allows developers to easily integrate new AI models and algorithms. Knowledge graph: Semantic Kernel provides a built-in knowledge graph that enables developers to store and query complex relationships between entities. Machine learning: Semantic Kernel supports various machine learning algorithms, including classification, regression, and clustering. Natural language processing: Semantic Kernel provides natural language processing capabilities, including text analysis and sentiment analysis. Integration with external services: Semantic Kernel allows developers to integrate with external services, such as databases and web services. Let's dive deep into writing some intelligent code using Semantic kernel C# SDK. I will write them in steps so it will be easy to follow along. Step 1: Setting up the Environment Let's set up our environment. You will need to install the following to follow along. .NET 8 or later Semantic Kernel SDK (available on NuGet) Your preferred IDE (Visual Studio, Visual Studio Code, etc.) Azure OpenAI access Step 2: Creating a New Project in VS Open Visual Studio and create a blank empty console DotNet 8 Application. Step 3: Install NuGet References Right-click on the project --> click on Manage NuGet reference section to install the below 2 latest NuGet packages. 1) Microsoft.SemanticKernel 2) Microsoft.Extensions.Configuration.json Note: To avoid Hardcoding Azure Open AI key and endpoint, I am storing these as key-value pair into appsettings.json, and using the #2 package, I can easily retrieve them based on the key. Step 4: Create and Deploy Azure OpenAI Model Once you have obtained access to Azure OpenAI service, login to the Azure portal or Azure OpenAI studio to create Azure OpenAI resource. The screenshots below are from the Azure portal: You can also create an Azure Open AI service resource using Azure CLI by running the following command: PowerShell az cognitiveservices account create -n <nameoftheresource> -g <Resourcegroupname> -l <location> \ --kind OpenAI --sku s0 --subscription subscriptionID You can see your resource from Azure OpenAI studio as well by navigating to this page and selecting the resource that was created from: Deploy a Model Azure OpenAI includes several types of base models as shown in the studio when you navigate to the Deployments tab. You can also create your own custom models by using existing base models as per your requirements. Let's use the deployed GPT-35-turbo model and see how to consume it in the Azure OpenAI studio. Fill in the details and click Create. Once the model is deployed, grab the Azure OpenAI key and endpoint to paste it inside the appsettings.json file as shown below Step 5: Create Kernel in the Code Step 6: Create a Plugin to Call the Azure OpenAI Model Step 7: Use Kernel To Invoke the LLM Models Once you run the program by pressing F5 you will see the response generated from the Azure OpenAI model. Complete Code C# using Microsoft.Extensions.Configuration; using Microsoft.SemanticKernel; var config = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .Build(); var builder = Kernel.CreateBuilder(); builder.Services.AddAzureOpenAIChatCompletion( deploymentName: config["AzureOpenAI:DeploymentModel"] ?? string.Empty, endpoint: config["AzureOpenAI:Endpoint"] ?? string.Empty, apiKey: config["AzureOpenAI:ApiKey"] ?? string.Empty); var semanticKernel = builder.Build(); Console.WriteLine(await semanticKernel.InvokePromptAsync("Give me shopping list for cooking Sushi")); Conclusion By combining AI LLM models with semantic kernels, you’ll create intelligent applications that go beyond simple keyword matching. Experiment, iterate, and keep learning to build remarkable apps that truly understand and serve your needs.
The rapid integration of artificial intelligence (AI) into software systems brings unprecedented opportunities and challenges for the software development community. As developers, we're not only responsible for building functional AI systems, but also for ensuring they operate safely, ethically, and responsibly. This article delves into the technical details of the NIST AI Risk Management Framework, providing concrete guidance for software developers building and deploying AI solutions. Image from NIST webpage The NIST framework lays out 4 important steps for AI developers to adopt to reduce the risk associated with AI. 1. Govern: Setting up the Fundamentals Governance is the most important and the foundation for this framework. Effective governance of AI risk starts with solid technical groundwork. In order to implement robust governance, developers of AI systems should explore some of the following approaches Version control and reproducibility: Implement rigorous version control for datasets, model architectures, training scripts, and configuration parameters. This ensures reproducibility, enabling tracking of changes, debugging issues, and auditing model behavior. Documentation and code review: Establish clear documentation requirements for all aspects of AI development. Conduct thorough code reviews to identify potential vulnerabilities, enforce coding best practices, and ensure adherence to established standards. Testing and validation frameworks: Build comprehensive testing frameworks to validate data quality, model performance, and system robustness. Employ unit tests, integration tests, and regression tests to catch errors early in the development cycle. Table 1: Examples of Technical Governance Approaches Aspect Approach Example Version Control Utilize Git for tracking code, data, and model versions. Document commit messages with specific changes, link to relevant issue trackers. Documentation Use Sphinx or MkDocs to generate documentation from code comments and Markdown files. Include API references, tutorials, and explanations of design decisions. Testing Employ frameworks like Pytest or JUnit for automated testing. Write tests for data loading, model training, prediction accuracy, and security vulnerabilities. 2. Map: Identifying Technical Risks in AI Systems Understanding the technical nuances of AI systems is crucial for identifying potential risks. Some of the key areas to explore for mapping the AI risks are: Data quality and bias: Assess the quality and representativeness of training data. Identify potential biases stemming from data collection, labeling, or sampling methodologies. Implement data pre-processing techniques (e.g., outlier detection, data cleaning) to mitigate data quality issues. Model robustness and adversarial attacks: Evaluate the vulnerability of AI models to adversarial examples – inputs designed to mislead the model. Implement adversarial training techniques to enhance model robustness and resilience against malicious inputs. Security vulnerabilities: Analyze the software architecture for security flaws. Implement secure coding practices to prevent common vulnerabilities like SQL injection, cross-site scripting, and authentication bypass. Employ penetration testing and vulnerability scanning tools to identify and address security weaknesses. Table 2: Examples of Technical Risk Identification Risk Category Description Example Data Bias Training data reflects historical or societal biases. An AI-powered credit card approval trained on data with historical bias against certain demographic groups might unfairly deny credit cards to individuals from those groups. Adversarial Attacks Maliciously crafted inputs designed to fool the model. An image recognition system could be tricked by an adversarial image to misclassify a positive as a negative result. Data Poisoning Injecting malicious data into the training dataset to compromise model performance. An attacker could insert corrupted data into a spam detection system's training set, causing it to misclassify spam messages as legitimate. 3. Measure: Evaluating and Measuring Technical Risks Evaluating the technical severity of risks requires quantitative metrics and rigorous analysis. A few metrics that we could deploy to measure the performance of AI include, Model performance metrics: Utilize relevant performance metrics to assess model accuracy, precision, recall, and F1 score. Monitor these metrics over time to detect performance degradation and identify potential retraining needs. Explainability and interpretability: Implement techniques like LIME (Local Interpretable Model-agnostic Explanations) or SHAP (SHapley Additive exPlanations) to understand model decision-making processes. Utilize visualization tools to interpret model behavior and identify potential biases. Security assessment tools: Employ static code analysis tools to identify security flaws in the source code. Use dynamic analysis tools (e.g., fuzzing, penetration testing) to uncover vulnerabilities in running systems. Table 3: Technical Risk Measurement Techniques Technique Description Example Confusion Matrix Visualizes the performance of a classification model by showing true positives, true negatives, false positives, and false negatives. Analyzing a confusion matrix can reveal if a model is consistently misclassifying certain categories, indicating potential bias. LIME Generates local explanations for model predictions by perturbing input features and observing the impact on the output. Using LIME, you can understand which features were most influential in a specific loan denial decision made by an AI model. Penetration Testing Simulates real-world attacks to identify security vulnerabilities in a system. A penetration test could uncover SQL injection vulnerabilities in an AI-powered chatbot, enabling attackers to steal user data. 4. Manage: Implementing Risk Controls Managing technical risks demands the implementation of robust controls and mitigation strategies. Some of the strategies to explore for managing the technical risks are Data de-biasing techniques: Implement techniques like re-weighting, data augmentation, or adversarial de-biasing to address biases in training data. if possible conduct fairness audits using appropriate metrics to evaluate the fairness of model outcomes. Secure software development practices: Adhere to secure coding principles to minimize security vulnerabilities. Use strong authentication mechanisms, encrypt sensitive data, and implement access control measures to safeguard systems and data. Model monitoring and anomaly detection: Establish continuous monitoring systems to track model performance and detect anomalies. Implement techniques like statistical process control or machine learning-based anomaly detection to identify deviations from expected behavior. Table 4: Technical Risk Mitigation Strategies Risk Mitigation Strategy Example Data Bias Data Augmentation: Generate synthetic data to increase the representation of underrepresented groups. Augment a dataset for facial recognition with synthetic images of individuals from diverse ethnic backgrounds to reduce bias. Adversarial Attacks Adversarial Training: Train the model on adversarial examples to improve its robustness against such attacks. Use adversarial training to improve the resilience of an image classification model against attacks that aim to manipulate image pixels. Data Poisoning Data Sanitization: Implement rigorous data validation and cleaning processes to detect and remove malicious data. Employ anomaly detection algorithms to identify and remove outliers or malicious data points injected into a training dataset. Conclusion As AI developers, we play a pivotal role in shaping the future of AI. By integrating the NIST AI Risk Management Framework into our development processes, we can build AI systems that are not only technically sound but also ethically responsible, socially beneficial, and worthy of public trust. This framework empowers us to address the technical complexities of AI risk, allowing us to create innovative solutions that benefit individuals, organizations, and society as a whole.
Machine learning continues to be one of the most rapidly advancing and in-demand fields of technology. Machine learning, a branch of artificial intelligence, enables computer systems to learn and adopt human-like qualities, ultimately leading to the development of artificially intelligent machines. Eight key human-like qualities that can be imparted to a computer using machine learning as part of the field of artificial intelligence are presented in the table below. Human Quality AI Discipline (using ML approach) Sight Computer Vision Speech Natural Language Processing (NLP) Locomotion Robotics Understanding Knowledge Representation and Reasoning Touch Haptics Emotional Intelligence Affective Computing (aka. Emotion AI) Creativity Generative Adversarial Networks (GANs) Decision-Making Reinforcement Learning However, the process of creating artificial intelligence requires large volumes of data. In machine learning, the more data that we have and train the model on, the better the model (AI agent) becomes at processing the given prompts or inputs and ultimately doing the task(s) for which it was trained. This data is not fed into the machine learning algorithms in its raw form. It (the data) must first undergo various inspections and phases of data cleansing and preparation before it is fed into the learning algorithms. We call this phase of the machine learning life cycle, the data preprocessing phase. As implied by the name, this phase consists of all the operations and procedures that will be applied to our dataset (rows/columns of values) to bring it into a cleaned state so that it will be accepted by the machine learning algorithm to start the training/learning process. This article will discuss and look at the most popular data preprocessing techniques used for machine learning. We will explore various methods to clean, transform, and scale our data. All exploration and practical examples will be done using Python code snippets to guide you with hands-on experience on how these techniques can be implemented effectively for your machine learning project. Why Preprocess Data? The literal holistic reason for preprocessing data is so that the data is accepted by the machine learning algorithm and thus, the training process can begin. However, if we look at the intrinsic inner workings of the machine learning framework itself, more reasons can be provided. The table below discusses the 5 key reasons (advantages) for preprocessing your data for the subsequent machine learning task. Reason Explanation Improved Data Quality Data Preprocessing ensures that your data is consistent, accurate, and reliable. Improved Model Performance Data Preprocessing allows your AI Model to capture trends and patterns on deeper and more accurate levels. Increased Accuracy Data Preprocessing allows the model evaluation metrics to be better and reflect a more accurate overview of the ML model. Decreased Training Time By feeding the algorithm data that has been cleaned, you are allowing the algorithm to run at its optimum level thereby reducing the computation time and removing unnecessary strain on computing resources. Feature Engineering By preprocessing your data, the machine learning practitioner can gauge the impact that certain features have on the model. This means that the ML practitioner can select the features that are most relevant for model construction. In its raw state, data can have a magnitude of errors and noise in it. Data preprocessing seeks to clean and free the data from these errors. Common challenges that are experienced with raw data include, but are not limited to, the following: Missing values: Null values or NaN (Not-a-Number) Noisy data: Outliers or incorrectly captured data points Inconsistent data: Different data formatting inside the same file Imbalanced data: Unequal class distributions (experienced in classification tasks) In the following sections of this article, we will proceed to work with hands-on examples of Data Preprocessing. Data Preprocessing Techniques in Python The frameworks that we will utilize to work with practical examples of data preprocessing: NumPy Pandas SciKit Learn Handling Missing Values The most popular techniques to handle missing values are removal and imputation. It is interesting to note that irrespective of what operation you are trying to perform if there is at least one null (NaN) inside your calculation or process, then the entire operation will fail and evaluate to a NaN (null/missing/error) value. Removal This is when we remove the rows or columns that contain the missing value(s). This is typically done when the proportion of missing data is relatively small compared to the entire dataset. Example Output Imputation This is when we replace the missing values in our data, with substituted values. The substituted value is commonly the mean, median, or mode of the data for that column. The term given to this process is imputation. Example Output Handling Noisy Data Our data is said to be noisy when we have outliers or irrelevant data points present. This noise can distort our model and therefore, our analysis. The common preprocessing techniques for handling noisy data include smoothing and binning. Smoothing This data preprocessing technique involves employing operations such as moving averages to reduce noise and identify trends. This allows for the essence of the data to be encapsulated. Example Output Binning This is a common process in statistics and follows the same underlying logic in machine learning data preprocessing. It involves grouping our data into bins to reduce the effect of minor observation errors. Example Output Data Transformation This data preprocessing technique plays a crucial role in helping to shape and guide algorithms that require numerical features as input, to optimum training. This is because data transformation deals with converting our raw data into a suitable format or range for our machine learning algorithm to work with. It is a crucial step for distance-based machine learning algorithms. The key data transformation techniques are normalization and standardization. As implied by the names of these operations, they are used to rescale the data within our features to a standard range or distribution. Normalization This data preprocessing technique will scale our data to a range of [0, 1] (inclusive of both numbers) or [-1, 1] (inclusive of both numbers). It is useful when our features have different ranges and we want to bring them to a common scale. Example Output Standardization Standardization will scale our data to have a mean of 0 and a standard deviation of 1. It is useful when the data contained within our features have different units of measurement or distribution. Example Output Encoding Categorical Data Our machine learning algorithms most often require the features matrix (input data) to be in the form of numbers, i.e., numerical/quantitative. However, our dataset may contain textual (categorical) data. Thus, all categorical (textual) data must be converted into a numerical format before feeding the data into the machine learning algorithm. The most commonly implemented techniques for handling categorical data include one-hot encoding (OHE) and label encoding. One-Hot Encoding This data preprocessing technique is employed to convert categorical values into binary vectors. This means that each unique category becomes its column inside the data frame, and the presence of the observation (row) containing that value or not, is represented by a binary 1 or 0 in the new column. Example Output Label Encoding This is when our categorical values are converted into integer labels. Essentially, each unique category is assigned a unique integer to represent hitherto. Example Output This tells us that the label encoding was done as follows: ‘Blue’ -> 0 ‘Green’ -> 1 ‘Red’ -> 2 P.S., the numerical assignment is Zero-Indexed (as with all collection types in Python) Feature Extraction and Selection As implied by the name of this data preprocessing technique, feature extraction/selection involves the machine learning practitioner selecting the most important features from the data, while feature extraction transforms the data into a reduced set of features. Feature Selection This data preprocessing technique helps us in identifying and selecting the features from our dataset that have the most significant impact on the model. Ultimately, selecting the best features will improve the performance of our model and reduce overfitting thereof. Correlation Matrix This is a matrix that helps us identify features that are highly correlated thereby allowing us to remove redundant features. “The correlation coefficients range from -1 to 1, where values closer to -1 or 1 indicate stronger correlation, while values closer to 0 indicate weaker or no correlation”. Example Output 1 Output 2 Chi-Square Statistic The Chi-Square Statistic is a test that measures the independence of two categorical variables. It is very useful when we are performing feature selection on categorical data. It calculates the p-value for our features which tells us how useful our features are for the task at hand. Example Output The output of the Chi-Square scores consists of two arrays: The first array contains the Chi-Square statistic values for each feature. The second array contains the p-values corresponding to each feature. In our example: For the first feature: The chi-square statistic value is 0.0 p-value is 1.0 For the second feature: The chi-square statistic value is 3.0 p-value is approximately 0.083 The Chi-Square statistic measures the association between the feature and the target variable. A higher Chi-Square value indicates a stronger association between the feature and the target. This tells us that the feature being analyzed is very useful in guiding the model to the desired target output. The p-value measures the probability of observing the Chi-Square statistic under the null hypothesis that the feature and the target are independent. Essentially, A low p-value (typically < 0.05) indicates that the association between the feature and the target is statistically significant. For our first feature, the Chi-Square value is 0.0, and the p-value is 1.0 thereby indicating no association with the target variable. For the second feature, the Chi-Square value is 3.0, and the corresponding p-value is approximately 0.083. This suggests that there might be some association between our second feature and the target variable. Keep in mind that we are working with dummy data and in the real world, the data will give you a lot more variation and points of analysis. Feature Extraction This is a data preprocessing technique that allows us to reduce the dimensionality of the data by transforming it into a new set of features. Logically speaking, model performance can be drastically increased by employing feature selection and extraction techniques. Principal Component Analysis (PCA) PCA is a data preprocessing dimensionality reduction technique that transforms our data into a set of right-angled (orthogonal) components thereby capturing the most variance present in our features. Example Output With this, we have successfully explored a variety of the most commonly used data preprocessing techniques that are used in Python machine learning tasks. Conclusion In this article, we explored popular data preprocessing techniques for machine learning with Python. We began by understanding the importance of data preprocessing and then looked at the common challenges associated with raw data. We then dove into various preprocessing techniques with hands-on examples in Python. Ultimately, data preprocessing is a step that cannot be skipped from your machine learning project lifecycle. Even if there are no changes or transformations to be made to your data, it is always worth the effort to apply these techniques to your data where applicable. because, in doing so, you will ensure that your data is cleaned and transformed for your machine learning algorithm and thus your subsequent machine learning model development factors such as model accuracy, computational complexity, and interpretability will see an improvement. In conclusion, data preprocessing lays the foundation for successful machine-learning projects. By paying attention to data quality and employing appropriate preprocessing techniques, we can unlock the full potential of our data and build models that deliver meaningful insights and actionable results. Code Python # -*- coding: utf-8 -*- """ @author: Karthik Rajashekaran """ # we import the necessary frameworks import pandas as pd import numpy as np # we create dummy data to work with data = {'A': [1, 2, None, 4], 'B': [5, None, None, 8], 'C': [10, 11, 12, 13]} # we create and print the dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # TECHNIQUE: ROW REMOVAL > we remove rows with any missing values df_cleaned = df.dropna() print("Row(s) With Null Value(s) Deleted:\n" + str(df_cleaned), "\n") # TECHNIQUE: COLUMN REMOVAL -> we remove columns with any missing values df_cleaned_columns = df.dropna(axis=1) print("Column(s) With Null Value(s) Deleted:\n" + str(df_cleaned_columns), "\n") #%% # IMPUTATION # we create dummy data to work with data = {'A': [1, 2, None, 4], 'B': [5, None, None, 8], 'C': [10, 11, 12, 13]} # we create and print the dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # we impute the missing values with mean df['A'] = df['A'].fillna(df['A'].mean()) df['B'] = df['B'].fillna(df['B'].median()) print("DataFrame After Imputation:\n" + str(df), "\n") #%% # SMOOTHING # we create dummy data to work with data = {'A': [1, 2, None, 4], 'B': [5, None, None, 8], 'C': [10, 11, 12, 13]} # we create and print the dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # we calculate the moving average for smoothing df['A_smoothed'] = df['A'].rolling(window=2).mean() print("Smoothed Column A DataFrame:\n" + str(df), "\n") #%% # BINNING # we create dummy data to work with data = {'A': [1, 2, None, 4], 'B': [5, None, None, 8], 'C': [10, 11, 12, 13]} # we create and print the dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # we bin the data into discrete intervals bins = [0, 5, 10, 15] labels = ['Low', 'Medium', 'High'] # we apply the binning on column 'C' df['Binned'] = pd.cut(df['C'], bins=bins, labels=labels) print("DataFrame Binned Column C:\n" + str(df), "\n") #%% # NORMALIZATION # we import the necessary frameworks from sklearn.preprocessing import MinMaxScaler import pandas as pd # we create dummy data to work with data = {'A': [1, 2, 3, 4, 5], 'B': [10, 20, 30, 40, 50]} # we print the original dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # we apply mix-max normalization to our data using sklearn scaler = MinMaxScaler() df_normalized = pd.DataFrame(scaler.fit_transform(df), columns=df.columns) print("Normalized DataFrame:\n" + str(df_normalized), "\n") #%% # STANDARDIZATION # we create dummy data to work with data = {'A': [1, 2, 3, 4, 5], 'B': [10, 20, 30, 40, 50]} # we print the original dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # we import 'StandardScaler' from sklearn from sklearn.preprocessing import StandardScaler # we apply standardization to our data scaler = StandardScaler() df_standardized = pd.DataFrame(scaler.fit_transform(df), columns=df.columns) print("Standardized DataFrame:\n" + str(df_standardized), "\n") #%% # ONE-HOT ENCODING # we import the necessary framework from sklearn.preprocessing import OneHotEncoder # we create dummy data to work with data = {'Color': ['Red', 'Blue', 'Green', 'Blue', 'Red']} # we print the original dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # we apply one-hot encoding to our categorical features encoder = OneHotEncoder(sparse_output=False) encoded_data = encoder.fit_transform(df[['Color']]) encoded_df = pd.DataFrame(encoded_data, columns=encoder.get_feature_names_out(['Color'])) print("OHE DataFrame:\n" + str(encoded_df), "\n") #%% # LABEL ENCODING # we import the necessary framework from sklearn.preprocessing import LabelEncoder # we create dummy data to work with data = {'Color': ['Red', 'Blue', 'Green', 'Blue', 'Red']} # we print the original dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # we apply label encoding to our dataframe label_encoder = LabelEncoder() df['Color_encoded'] = label_encoder.fit_transform(df['Color']) print("Label Encoded DataFrame:\n" + str(df), "\n") #%% # CORRELATION MATRIX # we import the necessary frameworks import pandas as pd import seaborn as sns import matplotlib.pyplot as plt # we create dummy data to work with data = {'A': [1, 2, 3, 4, 5], 'B': [10, 20, 30, 40, 50], 'C': [5, 4, 3, 2, 1]} # we print the original dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # we compute the correlation matrix of our features correlation_matrix = df.corr() # we visualize the correlation matrix sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm') plt.show() #%% # CHI-SQUARE STATISTIC # we import the necessary frameworks from sklearn.feature_selection import chi2 from sklearn.preprocessing import LabelEncoder import pandas as pd # we create dummy data to work with data = {'Feature1': [1, 2, 3, 4, 5], 'Feature2': ['A', 'B', 'A', 'B', 'A'], 'Label': [0, 1, 0, 1, 0]} # we print the original dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # we encode the categorical features in our dataframe label_encoder = LabelEncoder() df['Feature2_encoded'] = label_encoder.fit_transform(df['Feature2']) print("Encocded DataFrame:\n" + str(df), "\n") # we apply the chi-square statistic to our features X = df[['Feature1', 'Feature2_encoded']] y = df['Label'] chi_scores = chi2(X, y) print("Chi-Square Scores:", chi_scores) #%% # PRINCIPAL COMPONENT ANALYSIS # we import the necessary framework from sklearn.decomposition import PCA # we create dummy data to work with data = {'A': [1, 2, 3, 4, 5], 'B': [10, 20, 30, 40, 50], 'C': [5, 4, 3, 2, 1]} # we print the original dataframe for viewing df = pd.DataFrame(data) print("Original DataFrame:\n" + str(df), "\n") # we apply PCA to our features pca = PCA(n_components=2) df_pca = pd.DataFrame(pca.fit_transform(df), columns=['PC1', 'PC2']) # we print the dimensionality reduced features print("PCA Features:\n" + str(df_pca), "\n") References Datacamp, How to Learn Machine Learning in 2024, February 2024. [Online]. [Accessed: 30 May 2024]. Statista, Growth of worldwide machine learning (ML) market size from 2021 to 2030, 13 February 2024. [Online]. [Accessed: 30 May 2024]. Hurne M.v., What is affective computing/emotion AI? 03 May 2024. [Online]. [Accessed: 30 May 2024].
In recent years, Machine Learning (ML) has propelled software systems into new realms of capability. From revolutionizing medical assistance and personalized recommendations to enabling chatbots and self-driving cars, ML has become a cornerstone of modern technology. Despite these advancements, the path from an ML concept to a fully operational product is riddled with obstacles. Crafting an accurate model is challenging enough, but productionising it into a successful, robust product requires more than just a well-trained algorithm. The more critical aspects of building a scalable infrastructure, ensuring continuous monitoring, automated retraining, and data wrangling are often overlooked. This blog outlines some best practices to keep in mind while turning an ML idea into a reliable product, addressing the often-overlooked challenges and providing insights into building a sustainable ML-driven solution. While there are simply too many variables at play depending on the use case, we will focus on some key aspects. Setting Goals Before embarking on model development and deployment, it is crucial to establish goals for the overall system and comprehend the model's objective within that system. Given that the ML model typically serves as a component of a larger system, it is essential to recognize that model accuracy is not the sole priority. While accurate predictions are vital for many applications of ML in production systems, over-investing in model accuracy — such as by adding new features, training on more data, or employing a more complex model — can be costly. Further, understanding the overall product requirements, as well as latency and memory constraints, often limits the available model design choices. Often, other system components, such as incorporating explainability into the product or additional human oversight, can alleviate many issues arising from inaccurate predictions. Data Quality Data is a core component of model training, and its quality largely determines the accuracy of predictions. Effective data cleaning and feature engineering, such as removing outliers, normalizing values, using word embeddings, or employing more complex and domain-specific techniques, are critical steps to ensure the model is trained on high-quality data. It is essential to apply these wrangling techniques consistently during both offline training and online inference to maintain parity and ensure the model's performance remains robust in production. In production environments, a significant concern is the potential for data distribution to change over time, which may no longer align with the training data used during offline analysis. This phenomenon, known as data drift, can severely impact the model's predictive performance. Acquiring representative and generalizable training data is inherently challenging, making it crucial to implement dynamic data collection as part of the production pipeline. This can be achieved by storing data in a database or fetching it through queries, allowing the model to evolve and adapt to new data over time. To address data drift effectively, it is necessary to implement robust feature drift detection and monitoring mechanisms. These systems should be capable of identifying shifts in data distribution promptly, enabling timely interventions to recalibrate the model. Model Deployment Model deployment and serving, transition an ML model from offline development to a live production environment, a critical step to ensure the model functions as intended and addresses the challenges it was designed to solve. After developing, saving, and testing the model offline, the high-level steps for serving include: Writing inference functions to load the model, encode features from requests, and invoke the model to generate responses. Creating a Docker file to define all runtime dependencies and the serving script. Building, running, and deploying the model locally or in the cloud. Despite its apparent simplicity, this process involves numerous design decisions and trade-offs based on system requirements, model characteristics, component interactions, and organizational DevOps practices. Commonly, model inference is provided as a microservice, accessible via remote procedure calls, which allows for scalability and ease of model updates in case of frequent experimentation. Alternatively, models can also be embedded as client-side libraries, which may be suitable for applications requiring low-latency predictions. Ensuring that the deployment strategy aligns with the overall system architecture and business objectives is crucial. Observability/Testing in Production Once the model has been deployed, it is extremely essential to set up observability and testing in production to ensure that the model is accurate enough for practical use. Offline evaluation is often overly optimistic, and online performance is usually much lower. Therefore, it is necessary to establish monitoring pipelines that evaluate both model and business metrics to proactively catch data and concept drifts, feature mismatches, and model degradation. Degraded model outputs can inject a negative feedback loop in training, where low-quality outputs are used to retrain the model, accumulating errors over time. For monitoring and evaluation, production data serves as the ultimate unseen test data. For example, when data drift occurs, production data will always reflect the most recent relevant distribution. By evaluating model accuracy in production, we can obtain an accurate picture of whether our model generalizes well. Before deploying models fully into production, it is also common practice to set up shadow execution, where the model makes predictions on production data, but those predictions are not actually used in decision-making. This approach is used solely for model evaluation to catch unforeseen issues. For instance, shadow evaluations are often employed in autonomous driving systems to compare the autonomous system's proposed actions against those of a human. This practice helps ensure that the model performs reliably and safely in real-world scenarios. Automated Retraining In ML-enabled systems, it is crucial to design with the expectation that the model will require frequent updates. As improved feature engineering techniques or optimized hyperparameters are identified, or when new library updates are available, the model should be updated accordingly. Moreover, the model should be refreshed whenever new training data is obtained, particularly from production data as data distributions or labels evolve over time, necessitating regular retraining to maintain accuracy. Automating the pipeline streamlines these updates by eliminating manual errors and ensuring a robust infrastructure. This automation can be achieved by implementing a retraining Directed Acyclic Graph (DAG) in Airflow, which will kick off a pipeline running all the stages from data collection and feature engineering to model training and deployment on a scheduled basis. Conclusion In conclusion, developing and deploying ML models in production environments is a multifaceted yet essential process for the effective utilization of ML technology. This process encompasses several critical steps, including design, model development, containerization, and continuous monitoring and maintenance; and various challenges related to infrastructure, scalability, latency, etc. may arise. However, with meticulous planning and the appropriate tools and methodologies for deployment, testing, and monitoring, these challenges can be effectively addressed. This will not just ensure the seamless operation of ML models, but also enhance their ability to solve real-world problems.
The Traveling Salesman Problem (TSP) is one of the challenging problems in operations research. Basically, TSP is about optimizing the route for the salesperson to efficiently visit the list of cities and return to the starting point. Although the problem's premise is simple, its computational complexity makes it a significant focus for both theoretical studies and practical implementations. Understanding Traveling Salesman Problem's Complexity The primary challenge of the TSP stems from its combinatorial nature. If a salesman visits n cities, they must evaluate (n−1)!/2 possible routes due to the flexibility in choosing the starting point and reversing the route. This factorial increase means that even a small number of cities results in a massive number of possible routes. For instance, with 20 cities, the salesman must consider about 60 billion routes. This phenomenon, termed a "combinatorial explosion," makes brute force methods impractical for large datasets. Solving Traveling Salesman Problem With Branch and Bound The branch and bound algorithm widely solves optimization problems like the Traveling Salesman Problem. It systematically evaluates all possible alternatives to find the optimal solution, while simultaneously excluding large parts of the search space that are unlikely to produce the best solution. Representation of the Problem as a Decision Tree In the context of TSP, the problem can be visualized as a decision tree where each node represents a partial route taken by the salesperson. The root of the tree is the starting city, and each branch from a node represents a possible next city to visit, extending the partial route. For example, a salesman starts in San Francisco. The root node represents San Francisco. From there, the salesman might choose to visit Seattle, Denver, or Las Vegas next, each forming a new branch in the decision tree. Bounding At each node in the decision tree, the algorithm calculates a lower bound on the possible cost of completing the route from that node. This lower bound is an estimate of the minimum additional cost required to complete the tour starting from the partial route represented by the node. For example, if the salesman has already traveled from San Francisco to Seattle, the algorithm calculates the least cost required to visit the remaining cities (Denver and Las Vegas) and return back to San Francisco. This estimation helps in determining whether to explore further from the current node. Branching The algorithm extends the current node by generating its children, where each child node represents a new partial route that includes one additional city not yet visited. This step breaks the problem into smaller subproblems that can be explored independently. Continuing the example, if the salesman has traveled from San Francisco to Seattle, the next branching might include routes from Seattle to Denver or Seattle to Las Vegas. These new branches further extend the decision tree. Pruning To optimize the search process, the algorithm compares the lower bound of each node with the cost of the optimal solution found so far. If the lower bound of a node is greater than or equal to the cost of the current optimal solution, then that node and all its child nodes are pruned and not explored further. This technique significantly reduces the number of nodes that need to be explored, thereby narrowing down the search space. For example, suppose the current best solution has a cost of 25. If the lower bound calculated for the route San Francisco → Seattle → Denver is 30, then this route and its extensions can be pruned because they cannot yield a better solution than 25. Exploring Nodes The algorithm continues to explore the remaining nodes (those not pruned), repeating the branching and bounding process. It keeps track of the best solution found during the search. When all nodes are either pruned or explored, the best solution at that point is the optimal solution. Example Consider a simple TSP with four cities: San Francisco (A), Seattle (B), Denver (C), and Las Vegas (D). Initial Step Start at city A (San Francisco). Calculate the lower bound for the initial state using heuristic estimates or other methods. Branching from A Generate branches for possible next cities: B (Seattle), C (Denver), and D (Las Vegas). Calculating Bounds For each branch, calculate the lower bound on the cost to complete the tour. For example, the branch from A to B might have a lower bound that includes the minimum costs of visiting the remaining cities and returning to A. Pruning Suppose the branch A → B has a lower bound of 20, A → C has a lower bound of 35, and A → D has a lower bound of 15. If the current best solution has a cost of 15, prune branches A → B and A → C because their bounds exceed 15. Exploring Remaining Nodes Continue exploring from A → D. From D, branch out to C and B, and repeat the bounding and pruning process until all possibilities are either explored or pruned. By systematically applying these steps, the branch and bound method effectively narrows down the search space and finds the optimal solution without having to examine every possible route exhaustively. This is particularly powerful for problems like TSP, where the number of possible solutions grows factorially with the number of cities. Pseudo Code for Branch and Bound TSP Definitions distance_matrix: A 2D array representing the distances between city pairs cities: A list of city names best_tour: The best route found best_cost: The cost of the best route found stack: A data structure for depth-first search (DFS) Functions calculate_lower_bound(tour): Calculates the lower bound on the cost to complete the tour from a partial route Algorithm Python def branch_and_bound_tsp(distance_matrix): # Initialize best_tour as None best_tour = None # Initialize best_cost as infinity best_cost = float('inf') # Initialize stack with root node (0,) and cost 0 stack = [((0,), 0)] decision_tree = [] while stack: tour, cost = stack.pop() if len(tour) == len(distance_matrix): cost += distance_matrix[tour[-1]][tour[0]] # Complete the tour if cost < best_cost: best_cost = cost best_tour = tour else: remaining_cities = [i for i in range(len(distance_matrix)) if i not in tour] for city in remaining_cities: new_tour = tour + (city,) new_cost = cost + distance_matrix[tour[-1]][city] lower_bound = calculate_lower_bound(distance_matrix, new_tour) if lower_bound < best_cost: stack.append((new_tour, new_cost)) # Add node to decision tree: new_tour with label showing cities and cost decision_tree.append((new_tour, new_cost)) # Add edge to decision tree: from tour to new_tour return best_tour, best_cost, decision_tree def calculate_lower_bound(distance_matrix, tour): bound = 0 remaining_cities = [i for i in range(len(distance_matrix)) if i not in tour] # Add edges already in the tour for i in range(1, len(tour)): bound += distance_matrix[tour[i-1]][tour[i]] # Estimate the cost to complete the tour if remaining_cities: bound += min(distance_matrix[tour[-1]][city] for city in remaining_cities) for city in remaining_cities: if len(remaining_cities) > 1: bound += min(distance_matrix[city][i] for i in remaining_cities if i != city) else: bound += distance_matrix[city][tour[0]] # Include return to start city return bound # Example distance matrix and cities distance_matrix = [ [0, 20, 35, 15], # San Francisco [20, 0, 25, 30], # Seattle [35, 25, 0, 10], # Denver [15, 30, 10, 0] # Las Vegas ] cities = ['San Francisco', 'Seattle', 'Denver', 'Las Vegas'] best_tour, best_cost, decision_tree = branch_and_bound_tsp(distance_matrix) # Output the best tour and its cost print(f"Best tour: {' -> '.join(cities[i] for i in best_tour)}") print(f"Cost of the best tour: {best_cost}") Complexity of the Branch and Bound Algorithm While the branch and bound algorithm is a powerful tool, it faces significant challenges when applied to the Traveling Salesman Problem. Time Complexity In the worst-case scenario, the time complexity of the branch and bound algorithm is factorial, denoted as O(n!), where n represents the number of cities. The number of possible routes grows factorially with the number of cities. For example, with just four cities San Francisco (A), Seattle (B), Denver (C), and Las Vegas (D) there are 4! (24) possible routes to evaluate. This number becomes unmanageable as the number of cities increases. High Computational Cost Evaluating and pruning branches in the search tree requires significant computational resources. As the number of cities increases, the complexity and the time required to find the optimal solution will also increase. Inefficient Pruning B&B relies on heuristic methods to estimate lower bounds for pruning non-promising branches. If these heuristics are not effective, the algorithm may explore many unnecessary branches, further increasing computational time. Graph Neural Networks (GNN) To overcome the constraints of traditional methods, recent progress in machine learning, especially with Graph Neural Networks (GNNs), has presented a promising solution. GNNs leverage the intrinsic structure of graph data, introducing innovative techniques that improve upon existing algorithms like the Branch and Bound (B&B) method. By using the representational capabilities of graphs, GNNs can effectively model complex relationships, thereby enhancing computational efficiency and helping in more informed decision-making processes. Enhanced State Representation through Graph Encoding Graph Neural Networks (GNNs) can transform the TSP graph, where cities are represented as nodes and distances as edges, into high-dimensional embeddings. These embeddings effectively capture the complex relationships between cities, offering a richer representation than traditional methods. For instance, for cities like San Francisco, Seattle, Denver, and Las Vegas, GNNs can generate embeddings that reflect both distances and the overall topology, providing a more detailed state representation. Improved Heuristics for Initialization Starting the B&B algorithm with nearly optimal heuristic solutions can significantly speed up the process. GNNs can predict these initial solutions by using learned embeddings to estimate more accurate starting points or bounds. For example, a GNN trained on numerous TSP instances may learn that starting a route from San Francisco to Denver might be more efficient than other initial routes, even if raw distance data suggests otherwise. Better Decision-Making in Dynamic Branch Selection Choosing promising branches in the B&B process is vital for efficiency. GNNs help by providing embeddings that predict the potential success of branching decisions, focusing on more likely optimal branches and reducing the search space. For example, embeddings might indicate that a branch from Seattle to Denver holds more promise due to better intermediary connections or lower overall distance than a branch from Seattle to Las Vegas. This allows the B&B algorithm to prioritize more strategic routes. Efficient Pruning With Enhanced Bounding GNNs refine the bounding process in B&B by generating accurate heuristic estimates, enabling tighter and more effective bounds. For a partial route from San Francisco to Seattle, a GNN can provide a sophisticated estimate of the lower bound for completing the tour. This bound is based on an intricate understanding of the entire network's topology and distances, allowing the B&B algorithm to discard non-optimal paths more effectively. Pseudo Code for Integrating GNN With Branch and Bound for TSP Python def GNN_encode(distance_matrix): GNN_model = initialize_GNN_model() graph = convert_to_graph(distance_matrix) embeddings = GNN_model.generate_embeddings(graph) return embeddings def convert_to_graph(distance_matrix): graph = create_empty_graph() for i in range(len(distance_matrix)): for j in range(len(distance_matrix)): if i != j: add_edge(graph, i, j, distance_matrix[i][j]) return graph def initialize_GNN_model(): return create_GNN_model(layers, activations, learning_rate) def integrate_gnn_with_branch_and_bound_tsp(distance_matrix): best_tour, best_cost = None, float('inf') stack = [((0,), 0)] embeddings = GNN_encode(distance_matrix) while stack: tour, cost = stack.pop() if len(tour) == len(distance_matrix): complete_cost = cost + distance_matrix[tour[-1]][tour[0]] if complete_cost < best_cost: best_tour, best_cost = tour, complete_cost else: remaining_cities = [i for i in range(len(distance_matrix)) if i not in tour] for city in remaining_cities: new_tour = tour + (city,) new_cost = cost + distance_matrix[tour[-1]][city] lower_bound = calculate_lower_bound(new_tour, embeddings) if lower_bound < best_cost: stack.append((new_tour, new_cost)) return best_tour, best_cost def calculate_lower_bound(tour, embeddings): bound = sum(distance_matrix[tour[i-1]][tour[i]] for i in range(1, len(tour))) remaining_cities = [i for i in range(len(distance_matrix)) if i not in tour] if remaining_cities: bound += min(distance_matrix[tour[-1]][city] for city in remaining_cities) bound += sum(min(distance_matrix[city][i] for i in remaining_cities if i != city) for city in remaining_cities) bound += distance_matrix[remaining_cities[-1]][tour[0]] return bound # Example distance matrix and cities distance_matrix = [ [0, 20, 35, 15], # San Francisco [20, 0, 25, 30], # Seattle [35, 25, 0, 10], # Denver [15, 30, 10, 0] # Las Vegas ] cities = ['San Francisco', 'Seattle', 'Denver', 'Las Vegas'] # Run the integrated algorithm best_tour, best_cost = integrate_gnn_with_branch_and_bound_tsp(distance_matrix) # Output the best tour and its cost print(f"Best tour: {' -> '.join(cities[i] for i in best_tour)}") print(f"Cost of the best tour: {best_cost}") Future Scope and Prospects Combining GNNs with Reinforcement Learning (RL) can further improve the decision-making process in the B&B algorithm. RL can dynamically adjust strategies for exploring and pruning branches based on real-time feedback, leading to even more efficient solutions. Over time, RL agents can learn from past experiences and enhance their strategies, adapting to different instances of TSP and other optimization problems. Conclusion Integrating Graph Neural Networks (GNNs) with the Branch & Bound (B&B) algorithm represents a significant advancement in solving the Traveling Salesman Problem (TSP). This hybrid approach effectively addresses the scalability and efficiency limitations of traditional methods, paving the way for tackling various complex combinatorial challenges. As research continues, the synergy between GNNs, B&B, and other cutting-edge optimization techniques is expected to yield robust solutions for increasingly intricate and dynamic problems, fostering innovative applications across diverse fields.
With recent achievements and attention to LLMs and the resultant Artificial Intelligence “Summer,” there has been a renaissance in model training methods aimed at getting to the most optimal, performant model as quickly as possible. Much of this has been achieved through brute scale — more chips, more data, more training steps. However, many teams have been focused on how we can train these models more efficiently and intelligently to achieve the desired results. Training LLMs typically include the following phases: Pretraining: This initial phase lays the foundation, taking the model from a set of inert neurons to a basic language generator. While the model ingests vast amounts of data (e.g., the entire internet), the outputs at this stage are often nonsensical, though not entirely gibberish. Supervised Fine-Tuning (SFT): This phase elevates the model from its unintelligible state, enabling it to generate more coherent and useful outputs. SFT involves providing the model with specific examples of desired behavior, and teaching it what is considered "helpful, useful, and sensible." Models can be deployed and used in production after this stage. Reinforcement Learning (RL): Taking the model from "working" to "good," RL goes beyond explicit instruction and allows the model to learn implicit preferences and desires of users through labeled preference data. This enables developers to encourage desired behaviors without needing to explicitly define why those behaviors are preferred. In-context learning: Also known as prompt engineering, this technique allows users to directly influence model behavior at inference time. By employing methods like constraints and N-shot learning, users can fine-tune the model's output to suit specific needs and contexts. Note this is not an exhaustive list, there are many other methods and phases that may be incorporated into idiosyncratic training pipelines Introducing Reward and Reinforcement Learning Humans excel at pattern recognition, often learning and adapting without conscious effort. Our intellectual development can be seen as a continuous process of increasingly complex pattern recognition. A child learns not to jump in puddles after experiencing negative consequences, much like an LLM undergoing SFT. Similarly, a teenager observing social interactions learns to adapt their behavior based on positive and negative feedback – the essence of Reinforcement Learning. Reinforcement Learning in Practice: The Key Components Preference data: Reinforcement Learning in LLMs typically require multiple (often 2) example outputs and a prompt/input in order to demonstrate a ‘gradient’. This is intended to show that certain behaviors are preferred relative to others. As an example, in RLHF, human users may be presented with a prompt and two examples and asked to choose which they prefer, or in other methods, they may be presented with an output and asked to improve on it in some way (where the improved version will be captured as the ‘preferred’ option). Reward model: A reward model is trained directly on the preference data. For a set of responses to a given input, each response can be assigned a scalar value representing its ‘rank’ within the set (for binary examples, this can be 0 and 1). The reward model is then trained to predict these scalar values given a novel input and output pair. That is, the RM is able to reproduce or predict a user’s preference Generator model: This is the final intended artifact. In simplified terms, during the Reinforcement Training Process, the Generator model generates an output, which is then scored by the Reward Model, and the resultant reward is fed back to the algorithm which decides how to mutate the Generator Model. For example, the algorithm will update the model to increase the odds of generating a given output when provided a positive reward and do the opposite in a negative reward scenario. In the LLM landscape, RLHF has been a dominant force. By gathering large volumes of human preference data, RLHF has enabled significant advancements in LLM performance. However, this approach is expensive, time-consuming, and susceptible to biases and vulnerabilities. This limitation has spurred the exploration of alternative methods for obtaining reward information at scale, paving the way for the emergence of RLAIF – a revolutionary approach poised to redefine the future of AI development. Understanding RLAIF: A Technical Overview of Scaling LLM Alignment With AI Feedback The core idea behind RLAIF is both simple and profound: if LLMs can generate creative text formats like poems, scripts, and even code, why can't they teach themselves? This concept of self-improvement promises to unlock unprecedented levels of quality and efficiency, surpassing the limitations of RLHF. And this is precisely what researchers have achieved with RLAIF. As with any form of Reinforcement Learning, the key lies in assigning value to outputs and training a Reward Model to predict those values. RLAIF's innovation is the ability to generate these preference labels automatically, at scale, without relying on human input. While all LLMs ultimately stem from human-generated data in some form, RLAIF leverages existing LLMs as "teachers" to guide the training process, eliminating the need for continuous human labeling. Using this method, the authors have been able to achieve comparable or even better results from RLAIF as opposed to RLHF. See below the graph of ‘Harmless Response Rate’ comparing the various approaches: To achieve this, the authors developed a number of methodological innovations. In-context learning and prompt engineering: RLAIF leverages in-context learning and carefully designed prompts to elicit preference information from the teacher LLM. These prompts provide context, examples (for few-shot learning), and the samples to be evaluated. The teacher LLMs output then serves as the reward signal. Chain-of-thought reasoning: To enhance the teacher LLM's reasoning capabilities, RLAIF employs Chain-of-Thought (CoT) prompting. While the reasoning process itself isn't directly used, it leads to more informed and nuanced preference judgments from the teacher LLM. Addressing position bias: To mitigate the influence of response order on the teacher's preference, RLAIF averages preferences obtained from multiple prompts with varying response orders. To understand this a little more directly, imagine the AI you are trying to train as a student, learning and improving through a continuous feedback loop. And then imagine an off-the-shelf AI, that has been through extensive training already, as the teacher. The teacher rewards the student for taking certain actions, coming up with certain responses, and so on, and punishes it otherwise. The way it does this is by ‘testing’ the student, by giving it quizzes where the student must select the optimal response. These tests are generated via ‘contrastive’ prompts, where the teacher generates slightly variable responses by slightly varying prompts in order to generate the responses. For example, in the context of code generation, one prompt might encourage the LLM to generate efficient code, potentially at the expense of readability, while the other emphasizes code clarity and documentation. The teacher then assigns its own preference as the ‘ground truth’ and asks the Student to indicate what it thinks is the preferred output. By comparing the students’ responses under these contrasting prompts, RLAIF assesses which response better aligns with the desired attribute. The student, meanwhile, aims to maximize the accumulated reward. So every time it gets punished, it decides to change something about itself so it doesn’t make a mistake again, and get punished again. When it is rewarded, it aims to reinforce that behavior so it is more likely to reproduce the same response in the future. In this way, over successive quizzes, the student gets better and better and punished less and less. While punishments never go to zero, the Student does converge to some minimum which represents the optimal performance it is able to achieve. From there, future inferences made by the student are likely to be of much higher quality than if RLAIF was not employed. The evaluation of synthetic (LLM-generated) preference data is crucial for effective alignment. RLAIF utilizes a "self-rewarding" score, which compares the generation probabilities of two responses under contrastive prompts. This score reflects the relative alignment of each response with the desired attribute. Finally, Direct Preference Optimization (DPO), an efficient RL algorithm, leverages these self-rewarding scores to optimize the student model, encouraging it to generate responses that align with human values. DPO directly optimizes an LLM towards preferred responses without needing to explicitly train a separate reward model. RLAIF in Action: Applications and Benefits RLAIF's versatility extends to various tasks, including summarization, dialogue generation, and code generation. Research has shown that RLAIF can achieve comparable or even superior performance to RLHF, while significantly reducing the reliance on human annotations. This translates to substantial cost savings and faster iteration cycles, making RLAIF particularly attractive for rapidly evolving LLM development. Moreover, RLAIF opens doors to a future of "closed-loop" LLM improvement. As the student model becomes better aligned through RLAIF, it can, in turn, be used as a more reliable teacher model for subsequent RLAIF iterations. This creates a positive feedback loop, potentially leading to continual improvement in LLM alignment without additional human intervention. So how can you leverage RLAIF? It’s actually quite simple if you already have an RL pipeline: Prompt set: Start with a set of prompts designed to elicit the desired behaviors. Alternatively, you can utilize an off-the-shelf LLM to generate these prompts. Contrastive prompts: For each prompt, create two slightly varied versions that emphasize different aspects of the target behavior (e.g., helpfulness vs. safety). LLMs can also automate this process. Response generation: Capture the responses from the student LLM for each prompt variation. Preference elicitation: Create meta-prompts to obtain preference information from the teacher LLM for each prompt-response pair. RL pipeline integration: Utilize the resulting preference data within your existing RL pipeline to guide the student model's learning and optimization. Challenges and Limitations Despite its potential, RLAIF faces challenges that require further research. The accuracy of AI annotations remains a concern, as biases from the teacher LLM can propagate to the student model. Furthermore, biases incorporated into this preference data can eventually become ‘crystallized’ in the teacher LLM which makes it difficult to remove afterward. Additionally, studies have shown that RLAIF-aligned models can sometimes generate responses with factual inconsistencies or decreased coherence. This necessitates exploring techniques to improve the factual grounding and overall quality of the generated text. Addressing these issues necessitates exploring techniques to enhance the reliability, quality, and objectivity of AI feedback. Furthermore, the theoretical underpinnings of RLAIF require careful examination. While the effectiveness of self-rewarding scores has been demonstrated, further analysis is needed to understand its limitations and refine the underlying assumptions. Emerging Trends and Future Research RLAIF's emergence has sparked exciting research directions. Comparing it with other RL methods like Reinforcement Learning from Execution Feedback (RLEF) can provide valuable insights into their respective strengths and weaknesses. One direction involves investigating fine-grained feedback mechanisms that provide more granular rewards at the individual token level, potentially leading to more precise and nuanced alignment outcomes. Another promising avenue explores the integration of multimodal information, incorporating data from images and videos to enrich the alignment process and foster a more comprehensive understanding within LLMs. Drawing inspiration from human learning, researchers are also exploring the application of curriculum learning principles in RLAIF, gradually increasing the complexity of tasks to enhance the efficiency and effectiveness of the alignment process. Additionally, investigating the potential for a positive feedback loop in RLAIF, leading to continual LLM improvement without human intervention, represents a significant step towards a more autonomous and self-improving AI ecosystem. Furthermore, there may be an opportunity to improve the quality of this approach by grounding feedback in the real world. As an example, if the agent were able to execute code, perform real-world experiments, or integrate with a robotic system to ‘instantiate’ feedback in the real world to capture more objective feedback, it would be able to capture more accurate, and reliable preference information without losing scaling advantages. However, ethical considerations remain paramount. As RLAIF empowers LLMs to shape their own alignment, it's crucial to ensure responsible development and deployment. Establishing robust safeguards against potential misuse and mitigating biases inherited from teacher models are essential for building trust and ensuring the ethical advancement of this technology. As mentioned previously, RLAIF has the potential to propagate and amplify biases present in the source data, which must be carefully examined before scaling this approach. Conclusion: RLAIF as a Stepping Stone To Aligned AI RLAIF presents a powerful and efficient approach to LLM alignment, offering significant advantages over traditional RLHF methods. Its scalability, cost-effectiveness, and potential for self-improvement hold immense promise for the future of AI development. While acknowledging the current challenges and limitations, ongoing research efforts are actively paving the way for a more reliable, objective, and ethically sound RLAIF framework. As we continue to explore this exciting frontier, RLAIF stands as a stepping stone towards a future where LLMs seamlessly integrate with human values and expectations, unlocking the full potential of AI for the benefit of society.
By fetching data from the organization’s internal or proprietary sources, Retrieval Augmented Generation (RAG) extends the capabilities of FMs to specific domains, without needing to retrain the model. It is a cost-effective approach to improving model output so it remains relevant, accurate, and useful in various contexts. Knowledge Bases for Amazon Bedrock is a fully managed capability that helps you implement the entire RAG workflow from ingestion to retrieval and prompt augmentation without having to build custom integrations to data sources and manage data flows. With MongoDB Atlas vector store integration, you can build RAG solutions to securely connect your organization’s private data sources to FMs in Amazon Bedrock. Let's see how the MongoDB Atlas integration with Knowledge Bases can simplify the process of building RAG applications. Configure MongoDB Atlas MongoDB Atlas cluster creation on AWS process is well documented. Here are the high-level steps: This integration requires an Atlas cluster tier of at least M10. During cluster creation, choose an M10 dedicated cluster tier. Create a database and collection. For authentication, create a database user. Select Password as the Authentication Method. Grant the Read and write to any database role to the user. Modify the IP Access List – add IP address 0.0.0.0/0 to allow access from anywhere. For production deployments, AWS PrivateLink is the recommended way to have Amazon Bedrock establish a secure connection to your MongoDB Atlas cluster. Create the Vector Search Index in MongoDB Atlas Use the below definition to create a Vector Search index. { "fields": [ { "numDimensions": 1536, "path": "AMAZON_BEDROCK_CHUNK_VECTOR", "similarity": "cosine", "type": "vector" }, { "path": "AMAZON_BEDROCK_METADATA", "type": "filter" }, { "path": "AMAZON_BEDROCK_TEXT_CHUNK", "type": "filter" } ] } AMAZON_BEDROCK_TEXT_CHUNK – Contains the raw text for each data chunk. We are using cosine similarity and embeddings of size 1536 (we will choose the embedding model accordingly - in the the upcoming steps). AMAZON_BEDROCK_CHUNK_VECTOR – Contains the vector embedding for the data chunk. AMAZON_BEDROCK_METADATA – Contains additional data for source attribution and rich query capabilities. Configure the Knowledge Base in Amazon Bedrock Create an AWS Secrets Manager secret to securely store the MongoDB Atlas database user credentials. Create an Amazon Simple Storage Service (Amazon S3) storage bucket and upload any document(s) of your choice — Knowledge Base supports multiple file formats (including text, HTML, and CSV). Later, you will use the knowledge base to ask questions about the contents of these documents. Navigate to the Amazon Bedrock console and start configuring the knowledge base. In step 2, choose the S3 bucket you created earlier: Select Titan Embeddings G1 – Text embedding model MongoDB Atlas as the vector database. Enter the basic information for the MongoDB Atlas cluster along with the ARN of the AWS Secrets Manager secret you had created earlier. In the Metadata field mapping attributes, enter the vector store-specific details. They should match the vector search index definition you used earlier. Once the knowledge base is created, you need to synchronize the data source (S3 bucket data) with the MongoDB Atlas vector search index. Once that's done, you can check the MongoDB Atlas collection to verify the data. As per the index definition, the vector embeddings have been stored in AMAZON_BEDROCK_CHUNK_VECTOR along with the text chunk and metadata in AMAZON_BEDROCK_TEXT_CHUNK and AMAZON_BEDROCK_METADATA, respectively. Query the Knowledge Base You can now ask questions about your documents by querying the knowledge base — select Show source details to see the chunks cited for each footnote. You can also change the foundation model. For example, I switched to Claude 3 Sonnet. Use Retrieval APIs To Integrate Knowledge Base With Applications To build RAG applications on top of Knowledge Bases for Amazon Bedrock, you can use the RetrieveAndGenerate API which allows you to query the knowledge base and get a response. If you want to further customize your RAG solutions, consider using the Retrieve API, which returns the semantic search responses that you can use for the remaining part of the RAG workflow. More Configurations You can further customize your knowledge base queries using a different search type, additional filter, different prompt, etc. Conclusion Thanks to the MongoDB Atlas integration with Knowledge Bases for Amazon Bedrock, most of the heavy lifting is taken care of. Once the vector search index and knowledge base are configured, you can incorporate RAG into your applications. Behind the scenes, Amazon Bedrock will convert your input (prompt) into embeddings, query the knowledge base, augment the FM prompt with the search results as contextual information, and return the generated response. Happy building!
In an era of heightened data privacy concerns, the development of local Large Language Model (LLM) applications provides an alternative to cloud-based solutions. Ollama offers one solution, enabling LLMs to be downloaded and used locally. In this article, we'll explore how to use Ollama with LangChain and SingleStore using a Jupyter Notebook. The notebook file used in this article is available on GitHub. Introduction We'll use a Virtual Machine running Ubuntu 22.04.2 as our test environment. An alternative would be to use venv. Create a SingleStoreDB Cloud Account A previous article showed the steps required to create a free SingleStore Cloud account. We'll use Ollama Demo Group as our Workspace Group Name and ollama-demo as our Workspace Name. We’ll make a note of our password and host name. For this article, we'll temporarily allow access from anywhere by configuring the firewall under Ollama Demo Group > Firewall. For production environments, firewall rules should be added to provide increased security. Create a Database In our SingleStore Cloud account, let's use the SQL Editor to create a new database. Call this ollama_demo, as follows: SQL CREATE DATABASE IF NOT EXISTS ollama_demo; Install Jupyter From the command line, we’ll install the classic Jupyter Notebook, as follows: Shell pip install notebook Install Ollama We'll install Ollama, as follows: Shell curl -fsSL https://ollama.com/install.sh | sh Environment Variable Using the password and host information we saved earlier, we’ll create an environment variable to point to our SingleStore instance, as follows: Shell export SINGLESTOREDB_URL="admin:<password>@<host>:3306/ollama_demo" Replace <password> and <host> with the values for your environment. Launch Jupyter We are now ready to work with Ollama and we’ll launch Jupyter: Shell jupyter notebook Fill Out the Notebook First, some packages: Shell !pip install langchain ollama --quiet --no-warn-script-location Next, we’ll import some libraries: Python import ollama from langchain_community.vectorstores import SingleStoreDB from langchain_community.vectorstores.utils import DistanceStrategy from langchain_core.documents import Document from langchain_community.embeddings import OllamaEmbeddings We'll create embeddings using all-minilm (45 MB at the time of writing): Python ollama.pull("all-minilm") Example output: Plain Text {'status': 'success'} For our LLM we'll use llama2 (3.8 GB at the time of writing): Python ollama.pull("llama2") Example output: Plain Text {'status': 'success'} Next, we’ll use the example text from the Ollama website: Python documents = [ "Llamas are members of the camelid family meaning they're pretty closely related to vicuñas and camels", "Llamas were first domesticated and used as pack animals 4,000 to 5,000 years ago in the Peruvian highlands", "Llamas can grow as much as 6 feet tall though the average llama between 5 feet 6 inches and 5 feet 9 inches tall", "Llamas weigh between 280 and 450 pounds and can carry 25 to 30 percent of their body weight", "Llamas are vegetarians and have very efficient digestive systems", "Llamas live to be about 20 years old, though some only live for 15 years and others live to be 30 years old" ] embeddings = OllamaEmbeddings( model = "all-minilm", ) dimensions = len(embeddings.embed_query(documents[0])) docs = [Document(text) for text in documents] We’ll specify all-minilm for the embeddings, determine the number of dimensions returned for the first document, and convert the documents to the format required by SingleStore. Next, we’ll use LangChain: Python docsearch = SingleStoreDB.from_documents( docs, embeddings, table_name = "langchain_docs", distance_strategy = DistanceStrategy.EUCLIDEAN_DISTANCE, use_vector_index = True, vector_size = dimensions ) In addition to the documents and embeddings, we’ll provide the name of the table we want to use for storage, the distance strategy, that we want to use a vector index, and the vector size using the dimensions we previously determined. These and other options are explained in further detail in the LangChain documentation. Using the SQL Editor in SingleStore Cloud, let’s check the structure of the table created by LangChain: SQL USE ollama_demo; DESCRIBE langchain_docs; Example output: Plain Text +----------+------------------+------+------+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+------------------+------+------+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | content | longtext | YES | | NULL | | | vector | vector(384, F32) | NO | MUL | NULL | | | metadata | JSON | YES | | NULL | | +----------+------------------+------+------+---------+----------------+ We can see that a vector column with 384 dimensions was created for storing the embeddings. Let’s also quickly check the stored data: SQL USE ollama_demo; SELECT SUBSTRING(content, 1, 30) AS content, SUBSTRING(vector, 1, 30) AS vector FROM langchain_docs; Example output: Plain Text +--------------------------------+--------------------------------+ | content | vector | +--------------------------------+--------------------------------+ | Llamas weigh between 280 and 4 | [0.235754818,0.242168128,-0.26 | | Llamas were first domesticated | [0.153105229,0.219774529,-0.20 | | Llamas are vegetarians and hav | [0.285528302,0.10461951,-0.313 | | Llamas are members of the came | [-0.0174482632,0.173883006,-0. | | Llamas can grow as much as 6 f | [-0.0232818555,0.122274697,-0. | | Llamas live to be about 20 yea | [0.0260244086,0.212311044,0.03 | +--------------------------------+--------------------------------+ Finally, let’s check the vector index: SQL USE ollama_demo; SHOW INDEX FROM langchain_docs; Example output: Plain Text +----------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------------+---------+---------------+---------------------------------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Index_options | +----------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------------+---------+---------------+---------------------------------------+ | langchain_docs | 0 | PRIMARY | 1 | id | NULL | NULL | NULL | NULL | | COLUMNSTORE HASH | | | | | langchain_docs | 1 | vector | 1 | vector | NULL | NULL | NULL | NULL | | VECTOR | | | {"metric_type": "EUCLIDEAN_DISTANCE"} | | langchain_docs | 1 | __SHARDKEY | 1 | id | NULL | NULL | NULL | NULL | | METADATA_ONLY | | | | +----------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------------+---------+---------------+---------------------------------------+ We’ll now ask a question, as follows: Python prompt = "What animals are llamas related to?" docs = docsearch.similarity_search(prompt) data = docs[0].page_content print(data) Example output: Plain Text Llamas are members of the camelid family meaning they're pretty closely related to vicuñas and camels Next, we’ll use the LLM, as follows: Python output = ollama.generate( model = "llama2", prompt = f"Using this data: {data}. Respond to this prompt: {prompt}" ) print(output["response"]) Example output: Plain Text Llamas are members of the camelid family, which means they are closely related to other animals such as: 1. Vicuñas: Vicuñas are small, wild relatives of llamas and alpacas. They are native to South America and are known for their soft, woolly coats. 2. Camels: Camels are also members of the camelid family and are known for their distinctive humps on their backs. There are two species of camel: the dromedary and the Bactrian. 3. Alpacas: Alpacas are domesticated animals that are closely related to llamas and vicuñas. They are native to South America and are known for their soft, luxurious fur. So, in summary, llamas are related to vicuñas, camels, and alpacas. Summary In this article, we’ve seen that we can connect to SingleStore, store the documents and embeddings, ask questions about the data in the database, and use the power of LLMs locally through Ollama.
This is a simple example of a Firebase function that uses Genkit and Ollama to translate any test to Spanish. This project uses the following technologies: Firebase Functions Firebase Genkit Ollama This project uses the following Node.js Packages: @genkit-ai/firebase: Genkit Firebase SDK to be able to use Genkit in Firebase Functions genkitx-ollama: Genkit Ollama plugin to be able to use Ollama in Genkit @genkit-ai/ai, @genkit-ai/core and @genkit-ai/flow: Genkit AI Core SDK @genkit-ai/dotprompt: Plugin to use DotPrompt in Genkit Setup Clone this GitHub repository. Run npm install to install the dependencies in the functions folder. Run firebase login to log in to your Firebase account. Install genkit-cli by running npm install -g genkit. This repo is supposed to be used with Node.js version 20. Run the Firebase Emulator To run the function locally, run GENKIT_ENV=dev firebase emulators:start --inspect-functions. The emulator will be available at http://localhost:4000. Open Genkit UI Go to the functions folder and run genkit start --attach http://localhost:3100 --port 4001 to open the Genkit UI. The UI will be available at http://localhost:4001. Run Gemma With Ollama You will need to install Ollama by running brew install ollama and then run ollama run gemma to start the Ollama server. Code Explanation The code is in the functions/index.ts file. The function is called translatorFlow and it uses the Genkit SDK to translate the text into Spanish. First, we have to configure the Genkit SDK with the Ollama plugin: configureGenkit({ plugins: [ firebase(), ollama({ models: [{ name: 'gemma' }], serverAddress: 'http://127.0.0.1:11434', // default ollama local address }), ], logLevel: "debug", enableTracingAndMetrics: true, }); Then, we define the function - in the Gen AI Kit, they call it Flows. Flows are functions with some additional characteristics: they are strongly typed, streamable, locally and remotely callable, and fully observable. Firebase Genkit provides CLI and Developer UI tooling for working with flows (running, debugging, etc): export const translatorFlow = onFlow( { name: "translatorFlow", inputSchema: z.object({ text: z.string() }), outputSchema: z.string(), authPolicy: noAuth(), // Not requiring authentication, but you can change this. It is highly recommended to require authentication for production use cases. }, async (toTranslate) => { const prompt = `Translate this ${toTranslate.text} to Spanish. Autodetect the language.`; const llmResponse = await generate({ model: 'ollama/gemma', prompt: prompt, config: { temperature: 1, }, }); return llmResponse.text(); } ); As we saw above, we use Zod to define the input and output schema of the function. We also use the generate function from the Genkit SDK to generate the translation. We also have disabled the authentication for this function, but you can change this by changing the authPolicy property: firebaseAuth((user) => { if (!user.email_verified) { throw new Error('Verified email required to run flow'); } }); For the example above, you will need to import the firebaseAuth function from the @genkit-ai/firebase/auth package: import { firebaseAuth } from '@genkit-ai/firebase/auth'; Invoke the Function Locally Now you can invoke the function by running genkit flow:run translatorFlow '{"text":"hi"}' in the terminal. You can also make a curl command by running curl -X GET -H "Content-Type: application/json" -d '{"data": { "text": "hi" }' http://127.0.0.1:5001/<firebase-project>/<region>/translatorFlow in the terminal. For example: > curl -X GET -H "Content-Type: application/json" -d '{"data": { "text": "hi" }' http://127.0.0.1:5001/action-helloworld/us-central1/translatorFlow {"result":"Hola\n\nThe translation of \"hi\" to Spanish is \"Hola\"."} You can also use Postman or any other tool to make a GET request to the function: Deploy To deploy the function, run firebase deploy --only functions. You will need to change the Ollama URL in the function to the URL of the Ollama server. Conclusion As you can see, it is very easy to use Genkit and Ollama in Firebase Functions. You can use this example as a starting point to create your own functions using Genkit and Ollama. You can find the full code of this example in the GitHub repository (linked earlier). Happy coding! Resources Firebase Genkit Ollama Firebase Functions
Creating an API and an admin app using a framework takes too long, and is far too complex. AI and automation can create systems in minutes rather than weeks or months, dramatically simpler, and fully customizable with tools and approaches you already know. In this tutorial, we'll show how to create a complete system using VS Code, Copilot, and API Logic Server (open source). We'll then add business logic with rules, and use Python to add a custom endpoint and Kafka integration. Links are provided so you can execute these steps on your own. Overview As shown below, you can submit a Natural Language description of a database to Copilot. This creates a Python data model (SQLAlchemy classes). You then use API Logic Server CLI to create an executable project from the model. Alternatively, you can create a project by identifying an existing database. The project is executable, providing an API and an admin app, enabling agile collaboration and unblocking custom app dev. Figure 1: Overview Setup To begin, install Python and VSCode. Optionally, install Copilot: it's moderately priced and you can execute this tutorial without it. But, it provides the Natural Language services shown here - it's quite a lot of fun to explore, so you might just want to splurge and acquire it. Then, install the API Logic Server and start it: Shell python3 -m venv venv # windows: python -m venv venv source venv/bin/activate # windows: venv\Scripts\activate python -m pip install ApiLogicServer ApiLogicServer start This will launch the API Logic Server in VSCode. We've moved the Copilot chat pane to the right. Figure 2: API Logic Manager in your IDE 1. Create Your Model With Copilot The README page includes the Natural Language Text to supply to Copilot; paste it, and press enter. It's shown in the diagram below in dark gray ("Use SQLAlchemy to..."). Copilot creates the SQLAlchemy model code. Paste the generated code into a new model file called sample_ai.py (step 2 in Figure 3 below): Figure 3: Creating a project with Copilot and API Logic Server 2. Create a Project With API Logic Server Create your project (step 3 in Figure 3 above) by entering the following into the bottom terminal pane (als is a synonym for ApiLogicServer): Shell als create --project-name=sample_ai --from-model=sample_ai.py --db-url=sqlite API Logic Server uses SQLAlchemy (a popular Python ORM) to create a database from the Copilot model and then creates a fully formed project by reading the database schema. The project includes your data model classes, your API, and your admin app, fully configured for execution. Here the target database is SQLite; the same scenario will work for other databases as well, but you will need to replace "sqlite" with a full URI of your database. Create Project From Existing Database Alternatively, if you don't have Copilot, you can use an existing database, and create your project using the pre-installed SQLite database (1b in Figure 1): Shell als create --project-name=sample_ai --db-url=sqlite:///sample_ai.sqlite 3. Microservice Automation: Executable Project In either case, API Logic Server creates a fully formed project, ready to run, and launches it in another instance of VSCode: Figure 4: Created Project (new VSCode instance) Press F5 to start the server; launch the created admin app in your browser to explore your data and the API: The admin app, based on React Admin, provides data query/update services, with multi-table support for automatic joins and page navigations. This can kick-start agile business user collaboration, and provide back-office data functions. A JSON API, provides retrieval/update services, including support to choose columns and related data. This unblocks custom app dev, which is often compressed into the end of the project while waiting on custom API development. Compare automation to framework-based development: With a framework, you are ready to code. With automation, you are ready to run. Figure 5: Microservice Automation - created admin app and API So, we have working software: an admin app, for business user collaboration; an API, to unblock custom app dev. It was fast - only took a few moments - and simple - did not require months to learn a framework. 4a. Customize With Rules: Logic Automation Well, "fast and simple" is great, but it's little more than a stupid pet trick without logic and security. That's often nearly half the work of a transactional database application. API Logic Server contains a rule engine. You can declare rules in Python, using IDE code completion services. It also provides value: spreadsheet-like rules reduce logic code (half the app) by 40X. But, we can have much more fun. As shown below, we can ask Copilot to create these rules for us, and paste them into a pre-created file: Figure 6: Creating Rules with Copilot These 5 lines of code look just like the requirements, so the level of abstraction is remarkably high. These 5 declarative rules would have required 200 lines of traditional procedural code. And they are executable. They listen for SQLAlchemy ORM events, and fire in response to the actual changes in the transaction. Rules (and their overhead) are pruned if their referenced data is unchanged. Rules are debuggable. Standard logging depicts each rule firing, including the state of the row. And, you can use your debugger. Similar declarative rules are provided for row-level security, based on a user's roles. Authorization information can be obtained from a SQL database, or corporate stores such as LDAP or Active Directory. 4b. Customize With Python Automation is great, but let's face it: you can never automate everything. It's mandatory to have a customization capability that is standards-based — using our favorite IDE, standard languages like Python, and standard frameworks like SQLAlchemy and Flask. Let's examine 2 typical customizations: a custom endpoint, and Kafka integration. Custom API Endpoint Imagine accepting orders in a designated format from a B2B partner. The custom endpoint below performs the transformation using order_b2b_def (a declarative mapping definition, not shown), and saves it. This automatically activates the business rules above to check credit. Figure 7: Customization - Adding a Custom Endpoint Custom Logic for Kafka Integration Let's further imagine that accepted orders must be further transformed, and sent to shipping via a Kafka message. The screenshot below illustrates that, in addition to rules, you can provide Python code for business logic. Here you have all the power of Python libraries: Flask, SQLAlchemy, Kafka, etc. Figure 8: Customization - Kafka Integration 5. Deploy Value is not realized until the system is deployed, whether for final production, or early collaboration with stakeholders. API Logic Server creates scripts to containerize your project, and deploy to Azure with Docker Compose: Summary The screenshots above illustrate remarkable agility. This system might have taken weeks or months using conventional frameworks. But it's more than agility. The level of abstraction here is very high, bringing a level of simplicity that empowers you to create microservices - even if you are new to Python or frameworks such as Flask and SQLAlchemy. Instead of a complex framework, it's more like an appliance — just plug into your idea or existing database, and you have an executable, customizable project. There are 4 key elements that deliver this speed and simplicity: Natural Language Processing: Even savvy SQL programmers welcome syntax help. It's a dream come true to start from Natural Language, as provided by Copilot. Microservice automation: Instead of slow and complex framework coding, just plug into your database for an instant API and Admin App. Logic automation with declarative rules: Instead of tedious code that describes how logic operates, rules express what you want to accomplish, and reduce the backend half of your application by 40x. Extensibility: Finish the remaining elements with your IDE, Python, and standard packages such as Flask and SQLAlchemy. Automation empowers more people, to do more.
Tuhin Chattopadhyay
CEO at Tuhin AI Advisory and Professor of Practice,
JAGSoM
Yifei Wang
Senior Machine Learning Engineer,
Meta
Austin Gil
Developer Advocate,
Akamai
Tim Spann
Principal Developer Advocate,
Zilliz