AI Generated beer? Everything is possible with Generative Adversarial Networks
Introduction
Generative AI models have undeniably sparked a revolution across numerous industries. Problems that were once considered challenging in the design and production process of various products now appear insignificant in light of the vast possibilities presented by these models.
While exploring several applications of Generative AI models during the last few months, mostly for work and study related purposes, I started thinking about how they could be useful for my hobby, which is not other than Home brewing.
Home brewing, the hobby of making beer at home, has witnessed an ever increasing community globally during the last years. This community is characterized by its active engagement on various blogs, forums, and social media groups, where home brewers share recipes, tips, and techniques to continually improve the quality and variety of their beers.
Literally, there are millions of beer recipes out there which a home brewer can either brew as is, or use as a source of inspiration to brew a slightly different beer.
Thus, the most apparent application of Generative AI models in the context of home brewing is to use them in order to generate entirely new beer recipes. And this is where Generative Adversarial Networks come in.
But what Generative Adversarial Networks really are ?
Generative Adversarial Networks (GANs) are a kind of artificial neural networks that consist of two neural networks, known as the generator and the discriminator. What makes them so special and distinguishes them from other kinds of Neural Networks is that they are trained in an adversarial way.
This means that the generator generates synthetic data (initially starting from producing noise / random data) and the discriminator tries to distinguish between between real data and synthetic data generated by the generator. The generator and discriminator are trained in a process called adversarial training, where they compete against each other in a feedback loop. This means that the generator tries to generate better synthetic data at each training loop in order to “fool” the discriminator, while the discriminator tries to better distinguish between “real” and “fake” (synthetic) data.
Through this iterative process, GANs learn to generate realistic data that is similar to the training data (but never the same), making them powerful tools for generating new data samples. The data that they produce can be anything from images, music, videos or even beer recipes!
The dataset
So I first found a dataset containing many beer recipes (180k+) created by home brewers. The recipes are coming from a well-known site for home brewers called BrewersFriend.com and the dataset was available on Kaggle.
Preprocessing
Obviously, a vast amount of preprocessing was needed in order to be ready to feed the data in a GAN. So, I dropped some columns (Alcohol percentage, IBUs, beer style, Mash pH etc) that were irrelevant to the recipe, or that they can easily be calculated or measured in a systematic way given the ingredients, the quantities and the times/phases that they should be added during the brewing process. This choice also prevents data leakage as we want to avoid the network learning patterns related to the “metadata” of the beer that may cause noisy samples. In essence, we only want to keep the ingredients, the quantities and the times at which they should be added because this is what the beer is made from at its core.
Also, I only used a slice of the whole dataset, more specifically I kept only the “All grain” recipes (dataset included recipes for other brewing methods as well,like “Malt Extract”, but let’s say that “All grain” is the most common method in the home brewing world) in order to simplify the problems and restrict the cardinality of the possible ingredients involved.
Finally, in order to trim the dataset a bit more and simplify the problem, I only selected recipes that contained up to 5 hop varieties and up to 5 fermentable elements (aka malts). Those are 2 of the most important ingredients of beer (along with water and yeast).
The model
Despite I really enjoy designing and coding Neural Networks from scratch (especially the generative ones), this time I used a more ready solution.
In order to build the model I used the CTGAN library that provides Generative Adversarial Networks for synthetic data generation oriented to structured / tabular data (as the beer recipes data we have at our disposal).
CTGAN library offers a simple API through which almost any experienced Python developer can train Generative Adversarial Networks even without being experienced with Generative AI models or understanding the underlying peculiarities.
The main reason I used it is that it handles an important part of the preprocessing, and specially the one related to the encoding of categorical variables, relieving the developer from the burden of doing it manually or using another library.
CTGAN has 3 basic requirements:
- It does not accept missing values, which is very common for many machine learning methods. Thus, if there are missing values in the dataset they have to be imputed. As the majority of the features involved in our problem are discrete, I imputed them using the “most frequent” strategy of the scikit-learn SimpleImputer(). However, probably using a scikit-learn pipeline to impute continuous variables using the “mean” strategy and the discrete columns using the “most frequent” strategy would probably be more appropriate.
- Columns declared as “discrete” during the instantiation of the model have to contain either String of Integer values.
- Columns that are not declared as discrete are expected to be of type float.
The code for part of the preprocessing, imputation as well as the declaration and training of the Generative Adversarial Network can be seen in the snippet below:
Keep in mind that Generative Adversarial Networks are typically hard to train. This model took almost 2 hours to be trained for 45 epochs on a 10-Core / 32GB MacBook Pro M1 Max. However, the model started converging sufficiently after almost 20 epochs, as it can be observed from the learning curves (loss).
Results
After training the model for 45 epochs , I got the first results (10 samples shown in the table below).
The interesting thing is that, in brewing terms the recipes that the model produced are actually meaningful! There were no major “glitches” in terms of design and the recipes produced were almost ready to be brewed (probably after a bit of fine-tuning).
However, the recipes had an interesting variance in terms of the ingredients selected (e.g specific malts combined with hop varieties that are not that common but in reasonable ratios that definitely seemed interesting), which is an indication that the model can be surely used during the inspiration process.
In order to further validate the mode, I shared some of the generated recipes with some fellow home brewers and asked their opinion about them without telling them that they have been generated by an AI model.
Despite some of them had some minor comments or suggestions about the recipes and how they could be even better or more balanced, ALL of them thought that the recipes had been designed by a human being. Which probably means that the model successfully passes the Turing Test.
Conclusion
In this short article we demonstrated how we can easily train Generative Adversarial Networks in no more than 60 lines of code to produce synthetic structured data regarding beer recipes. This is only one application out of many that GANs have offered during the last years.
For home brewers it certainly worked and I am pretty confident that many other industries and areas may benefit from the generation of synthetic data of such high quality. Synthetic data can be useful in so many ways especially for inspiration, design or testing purposes.
The full code of the project is availalble on GitHub.
Cheers 🍻!