Name: Towards AI Legal Name: Towards AI, Inc. Description: Towards AI is the world's leading artificial intelligence (AI) and technology publication. Read by thought-leaders and decision-makers around the world. Phone Number: +1-650-246-9381 Email: pub@towardsai.net
228 Park Avenue South New York, NY 10003 United States
Website: Publisher: https://towardsai.net/#publisher Diversity Policy: https://towardsai.net/about Ethics Policy: https://towardsai.net/about Masthead: https://towardsai.net/about
Name: Towards AI Legal Name: Towards AI, Inc. Description: Towards AI is the world's leading artificial intelligence (AI) and technology publication. Founders: Roberto Iriondo, , Job Title: Co-founder and Advisor Works for: Towards AI, Inc. Follow Roberto: X, LinkedIn, GitHub, Google Scholar, Towards AI Profile, Medium, ML@CMU, FreeCodeCamp, Crunchbase, Bloomberg, Roberto Iriondo, Generative AI Lab, Generative AI Lab Denis Piffaretti, Job Title: Co-founder Works for: Towards AI, Inc. Louie Peters, Job Title: Co-founder Works for: Towards AI, Inc. Louis-François Bouchard, Job Title: Co-founder Works for: Towards AI, Inc. Cover:
Towards AI Cover
Logo:
Towards AI Logo
Areas Served: Worldwide Alternate Name: Towards AI, Inc. Alternate Name: Towards AI Co. Alternate Name: towards ai Alternate Name: towardsai Alternate Name: towards.ai Alternate Name: tai Alternate Name: toward ai Alternate Name: toward.ai Alternate Name: Towards AI, Inc. Alternate Name: towardsai.net Alternate Name: pub.towardsai.net
5 stars – based on 497 reviews

Frequently Used, Contextual References

TODO: Remember to copy unique IDs whenever it needs used. i.e., URL: 304b2e42315e

Resources

Take our 85+ lesson From Beginner to Advanced LLM Developer Certification: From choosing a project to deploying a working product this is the most comprehensive and practical LLM course out there!

Publication

Optimizing Object Avoidance With Genetic Algorithm in Python
Latest   Machine Learning

Optimizing Object Avoidance With Genetic Algorithm in Python

Last Updated on June 6, 2023 by Editorial Team

Author(s): Kong You Liow

Originally published on Towards AI.

Artist impression of DNA. Source: image by Gerd Altmann from Pixabay.

Nature has long served as a source of inspiration for optimization and problem-solving techniques. One such approach that emulates natural evolution is the genetic algorithm.

A genetic algorithm is a metaheuristic that leverages the principles of natural selection and genetic inheritance to uncover near-optimal or optimal solutions. At the core of every genetic algorithm lies the concept of a chromosome. Through processes that simulate genetic evolution, such as selection, crossover, and mutation, a set of chromosomes undergoes continuous refinement. Underperforming chromosomes are gradually eliminated, while those exhibiting better performance are retained. The fitness of the chromosomes serves as a measure of their effectiveness. Over several iterations, known as generations, a single or a group of chromosomes eventually emerges as the optimal or near-optimal solution to the given problem.

While the efficiency of the genetic algorithm may be a subject of debate, its straightforward implementation has made it a popular choice among modelers. This algorithm has found applications in various domains, including engineering, computer science, and finance, where it tackles optimization problems that are typically computationally challenging or difficult to solve.

This article demonstrates the principles of the genetic algorithm on a 2-dimensional obstacle avoidance problem. Our setup consists of a moving object (referred to as the car) that travels horizontally from left to right. Additionally, there are moving obstacles that traverse vertically, potentially obstructing the car’s path. The primary focus of our discussion centers around the algorithm itself, specifically its fundamental operators: selection, crossover, and mutation. The detailed explanation of the simulation setup will be addressed separately in future discussions.

Defining chromosome and fitness

In every genetic algorithm, the concepts of chromosomes and fitness play crucial roles. A chromosome is essentially an array of values, commonly expressed in bits (0s and 1s), although it can also be represented as integers depending on the specific problem. The chromosome represents the solution domain for the given problem.

On the other hand, fitness serves as a metric or function that evaluates the performance of a solution. It determines how well a particular chromosome or solution addresses the problem at hand. The fitness function provides a quantitative measure of the chromosome’s suitability or effectiveness within the context of the optimization problem.

In our simplified setup, we define a chromosome as the representation of the car’s acceleration throughout the simulation. Each value within the chromosome corresponds to the acceleration at a specific time. Let us consider an example chromosome:

>>> acceleration = [1, 2, -1, 2, 3]

In this scenario, our simulation spans five-time frames. The acceleration values within the chromosome are as follows: 1 for the first frame, 2 for the second frame, -1 for the third frame, and so on. We have chosen acceleration as the chromosome because it is the second derivative of the distance traveled, and integrating acceleration twice yields a smooth movement for the car.

The plots of acceleration, velocity, and position over time. Integrating integer-like acceleration twice results in a smooth position curve. Source: image by the author.

Next, we define fitness as the normalized distance traveled by the car. The simulation terminates either when the car collides with an obstacle or when it reaches the maximum number of time frames. The purpose of this fitness definition is to encourage the algorithm to prioritize chromosomes (acceleration sequences) which produce cars that can successfully navigate around obstacles. Ideally, a successful scenario occurs when the car completes the entire course within the given time frame, successfully avoiding all obstacles without any collisions.

Structure of genetic algorithm

We can summarise the steps of our genetic algorithm as follows:

  1. Initialization: begin by randomly generating a set of chromosomes.
  2. Fitness calculation: evaluate the fitness of each individual chromosome and identify the best-performing chromosome.
  3. Fitness probability calculation: determine the fitness probability for each chromosome.
  4. Selection: Choose a subset of stronger chromosomes based on their fitness probability and remove weaker chromosomes. Replace the removed chromosomes with newly generated random chromosomes.
  5. Crossover: Select pairs of chromosomes and exchange segments at corresponding array locations (alleles).
  6. Mutation: Randomly modify values within the set of chromosomes.
  7. Repeat Steps 2 to 6 until one of the termination conditions is met: either the car associated with the best-performing chromosome completes the course without collision and within the given time, or the maximum number of generations is reached.
A flow diagram of our genetic algorithm. Source: image by the author.

In our setup, we work with a fixed number of chromosomes, specifically 50 chromosomes in each set. Additionally, we set the maximum number of generations to 50 to prevent the simulation from running indefinitely without finding a solution. The maximum duration of the simulation is set to 400-time frames.

Fitness

The figure below illustrates the trajectory of the best-performing car, derived from the best-performing chromosome out of the initial set of 50 chromosomes in the 0th generation. The acceleration values in this chromosome are randomly generated. However, in this specific case, the car collides with the 9th obstacle, leading to the termination of the simulation. The total distance traveled by car in this scenario is 18 (in arbitrary distance units). As a result, the fitness score for this best-performing chromosome is approximately 7.5/18 = 0.417.

A simulation of the moving car avoiding obstacles in the 0th generation, in which the car collides with an obstacle. Source: animation by the author.

Fitness probability

Before proceeding to the core of the algorithm, we must calculate the fitness probability for each chromosome. The fitness probability is determined by normalizing the fitness values, providing an indication of how well each chromosome performs relative to others within the set. These probabilities are then utilized in the selection phase of the algorithm.

Consider the following example, which illustrates the fitness values and respective fitness probabilities for three chromosomes. In this scenario, Chromosome 1 demonstrates twice the performance of both Chromosome 2 and Chromosome 3.

A table to illustrate the calculation of fitness probabilities from fitness values. Source: image by the author.

Below is a code snippet demonstrating a Fitness class that includes methods to calculate the fitness values (normalized distances) and fitness probabilities for a given set of chromosomes:

class Fitness:
def __init__(
self,
distances: numpy.ndarray,
):
"""
:param distances: The distance travelled by the car for each
chromosome.
"""
self.distances = distances

def normalised_distance(self) -> numpy.ndarray:
"""
Use travel distances over maximum distance as fitnesses.
Shape of `fitnesses` is (N_chromosome,). `c.max_distance` = 18.
"""
return self.distances / c.max_distance

@staticmethod
def fitness_probabilities(fitnesses: numpy.ndarray) -> numpy.ndarray:
"""
Compute fitness probability.
"""
p_fitness = numpy.asarray(fitnesses) / numpy.sum(fitnesses)
return p_fitness

Selection

The first operator of the genetic algorithm is selection, which involves choosing well-performing chromosomes from the set for further processing. Various selection methods are available, some of which are problem-specific, while others are more general in nature. In our setup, we utilize the roulette wheel selection method, a commonly used approach where the probability of selecting a chromosome is directly proportional to its fitness probability.

The roulette wheel selection process is repeated until the desired number of chromosomes is obtained, maintaining the same quantity as in the original set. By employing this method, chromosomes with higher fitness probabilities have a greater chance of being selected, reflecting their superior performance.

In the previous example with three chromosomes, let us illustrate the selection process using the roulette wheel selection method. In this case, we draw chromosomes from the set with repetition, conducting three draws to obtain a new set of three chromosomes. Given that Chromosome 1 has the highest fitness probability, it has a greater chance of being selected during the roulette wheel selection process.

Let us simulate the draws: after the three draws, we have a new set of three chromosomes: Chromosome 1, Chromosome 1 (repeated selection), and Chromosome 3. The selection process favors chromosomes with higher fitness probabilities, resulting in a higher likelihood of selecting Chromosome 1 multiple times.

An example roulette wheel with three choices. Imagine rotating the roulette wheel for each draw. Source: image by the author.

Below is a code snippet illustrating a Selection class that implements the roulette wheel selection method using the numpy.random.choice() function:

class Selection:
def __init__(
self,
p_fitness: numpy.ndarray,
accelerations: numpy.ndarray,
):
"""
:param p_fitness: Fitness probabilities.
:param accelerations: Accelerations (a.k.a. chromosomes).
"""
self.p_fitness = p_fitness
self.accelerations = accelerations

def roulette_wheel(self) -> numpy.ndarray:
"""
Selection via roulette wheel. `c.N_chromosome` = 50.
"""
chromosome_to_keep_indices = numpy.random.choice(
c.N_chromosome, c.N_chromosome, True, self.p_fitness
)
return self.accelerations[chromosome_to_keep_indices]

Crossover

Following the selection process, the new set of chromosomes undergoes crossover, which involves generating new solutions that retain similarities to the previously selected chromosomes. In a crossover, alleles between a set of chromosomes are exchanged. Let us revisit our example of three chromosomes to illustrate a possible crossover operation:

An illustration of crossover. The colours indicate the pairs of values swapped. Source: image by the author.

In this example, the alleles at specific positions between a pair of chromosomes are swapped, such as the second value of Chromosome 1 and the second value of Chromosome 3, as well as the fifth value of Chromosome 2 and the fifth value of Chromosome 3. Additionally, crossover can also occur between Chromosome 1 and Chromosome 2 at positions where the resulting alleles would be identical. For instance, if the third value of Chromosome 1 and the third value of Chromosome 2 are the same, the crossover can still take place at that position.

To implement crossover, we begin by defining the probability of crossover. Typically, a small value is assigned to this probability to ensure that the previously selected chromosomes remain relatively intact, while still introducing stochasticity in the new set of chromosomes. In our algorithm, we set the probability of crossover to 0.2.

Below is a code snippet illustrating the Crossover class. The simple_crossover method randomly selects a certain number of chromosomes for crossover. For each pair of selected chromosomes, a random allele is chosen to be swapped.

class Crossover:
def __init__(self, accelerations: numpy.ndarray):
self.accelerations = accelerations

def simple_crossover(self) -> numpy.ndarray:
"""
Simple non-binary crossover. `c.p_crossover` = 0.2.
"""
N_crossover = int(c.p_crossover * c.N_chromosome)
if N_crossover % 2 != 0:
N_crossover -= 1

random_indices = numpy.random.choice(
c.N_chromosome, size=N_crossover, replace=False
)

# Crossover front and rear chromosome pairs
for n in range(int(N_crossover / 2)):
a, b = random_indices[n], random_indices[N_crossover - n - 1]
front = self.accelerations[a].copy()
rear = self.accelerations[b].copy()
portion = numpy.random.choice(c.N_frame, size=2, replace=False)
self.accelerations[a][portion[0] : portion[1]] = rear[
portion[0] : portion[1]
]
self.accelerations[b][portion[0] : portion[1]] = front[
portion[0] : portion[1]
]

return self.accelerations

Mutation

The final operator in the genetic algorithm is a mutation, which introduces randomness by altering the values within the chromosomes. Returning to our example of three chromosomes, a mutation operation could lead to the following updated set of chromosomes:

An illustration of mutation. The green boxes are the mutated values. Source: image by the author.

In this example, four mutations occur: the first value of Chromosome 1 mutates from 1 to 4, the third value of Chromosome 2 mutates from -1 to 0, the first value of Chromosome 3 mutates from 1 to 0, and the fourth value of Chromosome 3 mutates from 4 to 3.

To incorporate mutation in the genetic algorithm, we require the mutation probability. It is crucial to strike a balance when applying mutations, as excessive mutation can lead to the loss of valuable information. In our case, we use a mutation probability of 0.05.

Below is the Mutation class, which includes the simple_mutation method. This method determines the number of mutations to be applied and selects the chromosomes and mutation locations accordingly. Randomly generated values are then assigned to the mutation locations.

class Mutation:
"""
Class to handle mutation related algorithm
"""

def __init__(self, accelerations: numpy.ndarray):
self.accelerations = accelerations

def simple_mutation(self):
"""
Simple non-binary mutation. `c.p_mutation` = 0.02, and
`c.max_acceleration` = 5.
"""
N_mutation = int(c.N_chromosome * c.N_frame * c.p_mutation)

# Y (which chromosome) and X (where in chromosome) to be mutated
mutation_x = numpy.random.randint(c.N_frame, size=N_mutation)
mutation_y = numpy.random.randint(c.N_chromosome, size=N_mutation)

for i in range(N_mutation):
x, y = mutation_x[i], mutation_y[i]
self.accelerations[y, x] = (
numpy.random.randint(-c.max_acceleration, c.max_acceleration + 1)
)

return self.accelerations

Result and summary

We continue the iterative process of selection, crossover, and mutation until we reach a (near-) optimal solution, which in our case, is when the car successfully completes the obstacle course. The figure below illustrates the car’s successful completion in the 47th generation:

A simulation of the moving car avoiding obstacles in the 47th generation. The car successfully completes the course. Source: animation by the author.

The generations leading up to the 47th iteration involve the algorithm evolving and improving the set of chromosomes to find the best-performing solution. Through the repeated application of selection, crossover, and mutation, the algorithm gradually hones in on chromosomes that enable the car to navigate the course successfully. The specific number of generations required to reach the optimal solution can vary depending on factors such as the complexity of the problem, the population size, and the effectiveness of the selection, crossover, and mutation operators.

In conclusion, we have demonstrated the successful application of a genetic algorithm to solve an object avoidance optimization problem. The genetic algorithm, with its iterative process of selection, crossover, and mutation, has proven effective in improving the car’s ability to navigate through moving obstacles over successive generations.

However, it is important to recognize the limitations of the genetic algorithm. One limitation is its lack of adaptability to changes in the problem setup. The best-performing chromosome obtained from one specific obstacle configuration may not be optimal or even effective when the setup is altered. To address new setups, the simulation would need to be rerun from the beginning to allow the car to learn and adapt to the changes. Furthermore, the effectiveness of the genetic algorithm heavily depends on how chromosomes and fitness are defined. Careful consideration must be given to designing appropriate chromosome representations and fitness metrics to ensure accurate and meaningful optimization results.

Despite these limitations, the genetic algorithm has shown its potential to tackle complex and unstructured problems. It serves as a valuable tool for optimization in various domains, providing insights and solutions that may not be readily achievable through traditional approaches. With proper parameter tuning and problem-specific adaptations, the genetic algorithm can be a powerful tool in finding near-optimal solutions in a wide range of optimization scenarios.

Join thousands of data leaders on the AI newsletter. Join over 80,000 subscribers and keep up to date with the latest developments in AI. From research to projects and ideas. If you are building an AI startup, an AI-related product, or a service, we invite you to consider becoming a sponsor.

Published via Towards AI

Feedback ↓

Sign Up for the Course
`; } else { console.error('Element with id="subscribe" not found within the page with class "home".'); } } }); // Remove duplicate text from articles /* Backup: 09/11/24 function removeDuplicateText() { const elements = document.querySelectorAll('h1, h2, h3, h4, h5, strong'); // Select the desired elements const seenTexts = new Set(); // A set to keep track of seen texts const tagCounters = {}; // Object to track instances of each tag elements.forEach(el => { const tagName = el.tagName.toLowerCase(); // Get the tag name (e.g., 'h1', 'h2', etc.) // Initialize a counter for each tag if not already done if (!tagCounters[tagName]) { tagCounters[tagName] = 0; } // Only process the first 10 elements of each tag type if (tagCounters[tagName] >= 2) { return; // Skip if the number of elements exceeds 10 } const text = el.textContent.trim(); // Get the text content const words = text.split(/\s+/); // Split the text into words if (words.length >= 4) { // Ensure at least 4 words const significantPart = words.slice(0, 5).join(' '); // Get first 5 words for matching // Check if the text (not the tag) has been seen before if (seenTexts.has(significantPart)) { // console.log('Duplicate found, removing:', el); // Log duplicate el.remove(); // Remove duplicate element } else { seenTexts.add(significantPart); // Add the text to the set } } tagCounters[tagName]++; // Increment the counter for this tag }); } removeDuplicateText(); */ // Remove duplicate text from articles function removeDuplicateText() { const elements = document.querySelectorAll('h1, h2, h3, h4, h5, strong'); // Select the desired elements const seenTexts = new Set(); // A set to keep track of seen texts const tagCounters = {}; // Object to track instances of each tag // List of classes to be excluded const excludedClasses = ['medium-author', 'post-widget-title']; elements.forEach(el => { // Skip elements with any of the excluded classes if (excludedClasses.some(cls => el.classList.contains(cls))) { return; // Skip this element if it has any of the excluded classes } const tagName = el.tagName.toLowerCase(); // Get the tag name (e.g., 'h1', 'h2', etc.) // Initialize a counter for each tag if not already done if (!tagCounters[tagName]) { tagCounters[tagName] = 0; } // Only process the first 10 elements of each tag type if (tagCounters[tagName] >= 10) { return; // Skip if the number of elements exceeds 10 } const text = el.textContent.trim(); // Get the text content const words = text.split(/\s+/); // Split the text into words if (words.length >= 4) { // Ensure at least 4 words const significantPart = words.slice(0, 5).join(' '); // Get first 5 words for matching // Check if the text (not the tag) has been seen before if (seenTexts.has(significantPart)) { // console.log('Duplicate found, removing:', el); // Log duplicate el.remove(); // Remove duplicate element } else { seenTexts.add(significantPart); // Add the text to the set } } tagCounters[tagName]++; // Increment the counter for this tag }); } removeDuplicateText(); //Remove unnecessary text in blog excerpts document.querySelectorAll('.blog p').forEach(function(paragraph) { // Replace the unwanted text pattern for each paragraph paragraph.innerHTML = paragraph.innerHTML .replace(/Author\(s\): [\w\s]+ Originally published on Towards AI\.?/g, '') // Removes 'Author(s): XYZ Originally published on Towards AI' .replace(/This member-only story is on us\. Upgrade to access all of Medium\./g, ''); // Removes 'This member-only story...' }); //Load ionic icons and cache them if ('localStorage' in window && window['localStorage'] !== null) { const cssLink = 'https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css'; const storedCss = localStorage.getItem('ionicons'); if (storedCss) { loadCSS(storedCss); } else { fetch(cssLink).then(response => response.text()).then(css => { localStorage.setItem('ionicons', css); loadCSS(css); }); } } function loadCSS(css) { const style = document.createElement('style'); style.innerHTML = css; document.head.appendChild(style); } //Remove elements from imported content automatically function removeStrongFromHeadings() { const elements = document.querySelectorAll('h1, h2, h3, h4, h5, h6, span'); elements.forEach(el => { const strongTags = el.querySelectorAll('strong'); strongTags.forEach(strongTag => { while (strongTag.firstChild) { strongTag.parentNode.insertBefore(strongTag.firstChild, strongTag); } strongTag.remove(); }); }); } removeStrongFromHeadings(); "use strict"; window.onload = () => { /* //This is an object for each category of subjects and in that there are kewords and link to the keywods let keywordsAndLinks = { //you can add more categories and define their keywords and add a link ds: { keywords: [ //you can add more keywords here they are detected and replaced with achor tag automatically 'data science', 'Data science', 'Data Science', 'data Science', 'DATA SCIENCE', ], //we will replace the linktext with the keyword later on in the code //you can easily change links for each category here //(include class="ml-link" and linktext) link: 'linktext', }, ml: { keywords: [ //Add more keywords 'machine learning', 'Machine learning', 'Machine Learning', 'machine Learning', 'MACHINE LEARNING', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, ai: { keywords: [ 'artificial intelligence', 'Artificial intelligence', 'Artificial Intelligence', 'artificial Intelligence', 'ARTIFICIAL INTELLIGENCE', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, nl: { keywords: [ 'NLP', 'nlp', 'natural language processing', 'Natural Language Processing', 'NATURAL LANGUAGE PROCESSING', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, des: { keywords: [ 'data engineering services', 'Data Engineering Services', 'DATA ENGINEERING SERVICES', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, td: { keywords: [ 'training data', 'Training Data', 'training Data', 'TRAINING DATA', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, ias: { keywords: [ 'image annotation services', 'Image annotation services', 'image Annotation services', 'image annotation Services', 'Image Annotation Services', 'IMAGE ANNOTATION SERVICES', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, l: { keywords: [ 'labeling', 'labelling', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, pbp: { keywords: [ 'previous blog posts', 'previous blog post', 'latest', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, mlc: { keywords: [ 'machine learning course', 'machine learning class', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, }; //Articles to skip let articleIdsToSkip = ['post-2651', 'post-3414', 'post-3540']; //keyword with its related achortag is recieved here along with article id function searchAndReplace(keyword, anchorTag, articleId) { //selects the h3 h4 and p tags that are inside of the article let content = document.querySelector(`#${articleId} .entry-content`); //replaces the "linktext" in achor tag with the keyword that will be searched and replaced let newLink = anchorTag.replace('linktext', keyword); //regular expression to search keyword var re = new RegExp('(' + keyword + ')', 'g'); //this replaces the keywords in h3 h4 and p tags content with achor tag content.innerHTML = content.innerHTML.replace(re, newLink); } function articleFilter(keyword, anchorTag) { //gets all the articles var articles = document.querySelectorAll('article'); //if its zero or less then there are no articles if (articles.length > 0) { for (let x = 0; x < articles.length; x++) { //articles to skip is an array in which there are ids of articles which should not get effected //if the current article's id is also in that array then do not call search and replace with its data if (!articleIdsToSkip.includes(articles[x].id)) { //search and replace is called on articles which should get effected searchAndReplace(keyword, anchorTag, articles[x].id, key); } else { console.log( `Cannot replace the keywords in article with id ${articles[x].id}` ); } } } else { console.log('No articles found.'); } } let key; //not part of script, added for (key in keywordsAndLinks) { //key is the object in keywords and links object i.e ds, ml, ai for (let i = 0; i < keywordsAndLinks[key].keywords.length; i++) { //keywordsAndLinks[key].keywords is the array of keywords for key (ds, ml, ai) //keywordsAndLinks[key].keywords[i] is the keyword and keywordsAndLinks[key].link is the link //keyword and link is sent to searchreplace where it is then replaced using regular expression and replace function articleFilter( keywordsAndLinks[key].keywords[i], keywordsAndLinks[key].link ); } } function cleanLinks() { // (making smal functions is for DRY) this function gets the links and only keeps the first 2 and from the rest removes the anchor tag and replaces it with its text function removeLinks(links) { if (links.length > 1) { for (let i = 2; i < links.length; i++) { links[i].outerHTML = links[i].textContent; } } } //arrays which will contain all the achor tags found with the class (ds-link, ml-link, ailink) in each article inserted using search and replace let dslinks; let mllinks; let ailinks; let nllinks; let deslinks; let tdlinks; let iaslinks; let llinks; let pbplinks; let mlclinks; const content = document.querySelectorAll('article'); //all articles content.forEach((c) => { //to skip the articles with specific ids if (!articleIdsToSkip.includes(c.id)) { //getting all the anchor tags in each article one by one dslinks = document.querySelectorAll(`#${c.id} .entry-content a.ds-link`); mllinks = document.querySelectorAll(`#${c.id} .entry-content a.ml-link`); ailinks = document.querySelectorAll(`#${c.id} .entry-content a.ai-link`); nllinks = document.querySelectorAll(`#${c.id} .entry-content a.ntrl-link`); deslinks = document.querySelectorAll(`#${c.id} .entry-content a.des-link`); tdlinks = document.querySelectorAll(`#${c.id} .entry-content a.td-link`); iaslinks = document.querySelectorAll(`#${c.id} .entry-content a.ias-link`); mlclinks = document.querySelectorAll(`#${c.id} .entry-content a.mlc-link`); llinks = document.querySelectorAll(`#${c.id} .entry-content a.l-link`); pbplinks = document.querySelectorAll(`#${c.id} .entry-content a.pbp-link`); //sending the anchor tags list of each article one by one to remove extra anchor tags removeLinks(dslinks); removeLinks(mllinks); removeLinks(ailinks); removeLinks(nllinks); removeLinks(deslinks); removeLinks(tdlinks); removeLinks(iaslinks); removeLinks(mlclinks); removeLinks(llinks); removeLinks(pbplinks); } }); } //To remove extra achor tags of each category (ds, ml, ai) and only have 2 of each category per article cleanLinks(); */ //Recommended Articles var ctaLinks = [ /* ' ' + '

Subscribe to our AI newsletter!

' + */ '

Take our 85+ lesson From Beginner to Advanced LLM Developer Certification: From choosing a project to deploying a working product this is the most comprehensive and practical LLM course out there!

'+ '

Towards AI has published Building LLMs for Production—our 470+ page guide to mastering LLMs with practical projects and expert insights!

' + '
' + '' + '' + '

Note: Content contains the views of the contributing authors and not Towards AI.
Disclosure: This website may contain sponsored content and affiliate links.

' + 'Discover Your Dream AI Career at Towards AI Jobs' + '

Towards AI has built a jobs board tailored specifically to Machine Learning and Data Science Jobs and Skills. Our software searches for live AI jobs each hour, labels and categorises them and makes them easily searchable. Explore over 10,000 live jobs today with Towards AI Jobs!

' + '
' + '

🔥 Recommended Articles 🔥

' + 'Why Become an LLM Developer? Launching Towards AI’s New One-Stop Conversion Course'+ 'Testing Launchpad.sh: A Container-based GPU Cloud for Inference and Fine-tuning'+ 'The Top 13 AI-Powered CRM Platforms
' + 'Top 11 AI Call Center Software for 2024
' + 'Learn Prompting 101—Prompt Engineering Course
' + 'Explore Leading Cloud Providers for GPU-Powered LLM Training
' + 'Best AI Communities for Artificial Intelligence Enthusiasts
' + 'Best Workstations for Deep Learning
' + 'Best Laptops for Deep Learning
' + 'Best Machine Learning Books
' + 'Machine Learning Algorithms
' + 'Neural Networks Tutorial
' + 'Best Public Datasets for Machine Learning
' + 'Neural Network Types
' + 'NLP Tutorial
' + 'Best Data Science Books
' + 'Monte Carlo Simulation Tutorial
' + 'Recommender System Tutorial
' + 'Linear Algebra for Deep Learning Tutorial
' + 'Google Colab Introduction
' + 'Decision Trees in Machine Learning
' + 'Principal Component Analysis (PCA) Tutorial
' + 'Linear Regression from Zero to Hero
'+ '

', /* + '

Join thousands of data leaders on the AI newsletter. It’s free, we don’t spam, and we never share your email address. Keep up to date with the latest work in AI. From research to projects and ideas. If you are building an AI startup, an AI-related product, or a service, we invite you to consider becoming a sponsor.

',*/ ]; var replaceText = { '': '', '': '', '
': '
' + ctaLinks + '
', }; Object.keys(replaceText).forEach((txtorig) => { //txtorig is the key in replacetext object const txtnew = replaceText[txtorig]; //txtnew is the value of the key in replacetext object let entryFooter = document.querySelector('article .entry-footer'); if (document.querySelectorAll('.single-post').length > 0) { //console.log('Article found.'); const text = entryFooter.innerHTML; entryFooter.innerHTML = text.replace(txtorig, txtnew); } else { // console.log('Article not found.'); //removing comment 09/04/24 } }); var css = document.createElement('style'); css.type = 'text/css'; css.innerHTML = '.post-tags { display:none !important } .article-cta a { font-size: 18px; }'; document.body.appendChild(css); //Extra //This function adds some accessibility needs to the site. function addAlly() { // In this function JQuery is replaced with vanilla javascript functions const imgCont = document.querySelector('.uw-imgcont'); imgCont.setAttribute('aria-label', 'AI news, latest developments'); imgCont.title = 'AI news, latest developments'; imgCont.rel = 'noopener'; document.querySelector('.page-mobile-menu-logo a').title = 'Towards AI Home'; document.querySelector('a.social-link').rel = 'noopener'; document.querySelector('a.uw-text').rel = 'noopener'; document.querySelector('a.uw-w-branding').rel = 'noopener'; document.querySelector('.blog h2.heading').innerHTML = 'Publication'; const popupSearch = document.querySelector$('a.btn-open-popup-search'); popupSearch.setAttribute('role', 'button'); popupSearch.title = 'Search'; const searchClose = document.querySelector('a.popup-search-close'); searchClose.setAttribute('role', 'button'); searchClose.title = 'Close search page'; // document // .querySelector('a.btn-open-popup-search') // .setAttribute( // 'href', // 'https://medium.com/towards-artificial-intelligence/search' // ); } // Add external attributes to 302 sticky and editorial links function extLink() { // Sticky 302 links, this fuction opens the link we send to Medium on a new tab and adds a "noopener" rel to them var stickyLinks = document.querySelectorAll('.grid-item.sticky a'); for (var i = 0; i < stickyLinks.length; i++) { /* stickyLinks[i].setAttribute('target', '_blank'); stickyLinks[i].setAttribute('rel', 'noopener'); */ } // Editorial 302 links, same here var editLinks = document.querySelectorAll( '.grid-item.category-editorial a' ); for (var i = 0; i < editLinks.length; i++) { editLinks[i].setAttribute('target', '_blank'); editLinks[i].setAttribute('rel', 'noopener'); } } // Add current year to copyright notices document.getElementById( 'js-current-year' ).textContent = new Date().getFullYear(); // Call functions after page load extLink(); //addAlly(); setTimeout(function() { //addAlly(); //ideally we should only need to run it once ↑ }, 5000); }; function closeCookieDialog (){ document.getElementById("cookie-consent").style.display = "none"; return false; } setTimeout ( function () { closeCookieDialog(); }, 15000); console.log(`%c 🚀🚀🚀 ███ █████ ███████ █████████ ███████████ █████████████ ███████████████ ███████ ███████ ███████ ┌───────────────────────────────────────────────────────────────────┐ │ │ │ Towards AI is looking for contributors! │ │ Join us in creating awesome AI content. │ │ Let's build the future of AI together → │ │ https://towardsai.net/contribute │ │ │ └───────────────────────────────────────────────────────────────────┘ `, `background: ; color: #00adff; font-size: large`); //Remove latest category across site document.querySelectorAll('a[rel="category tag"]').forEach(function(el) { if (el.textContent.trim() === 'Latest') { // Remove the two consecutive spaces (  ) if (el.nextSibling && el.nextSibling.nodeValue.includes('\u00A0\u00A0')) { el.nextSibling.nodeValue = ''; // Remove the spaces } el.style.display = 'none'; // Hide the element } }); // Add cross-domain measurement, anonymize IPs 'use strict'; //var ga = gtag; ga('config', 'G-9D3HKKFV1Q', 'auto', { /*'allowLinker': true,*/ 'anonymize_ip': true/*, 'linker': { 'domains': [ 'medium.com/towards-artificial-intelligence', 'datasets.towardsai.net', 'rss.towardsai.net', 'feed.towardsai.net', 'contribute.towardsai.net', 'members.towardsai.net', 'pub.towardsai.net', 'news.towardsai.net' ] } */ }); ga('send', 'pageview'); -->