Evolving Consciousness: A Journey Towards Artificial Conscious Intelligence (ACI) with Genetic Algorithms

John Gardner (Sinjhin)
5 min readMar 23, 2024

In the early 2000’s, before neural networks became readily accessible, the prevailing approach relied heavily on brute-force methods, and it was only said half-jokingly that with “enough if and else-if statements” we could accomplish AI… I questioned the arrogance of assuming we had a monopoly on intelligence, let alone its definition. While having the medium of symbology and language is necessary to measure results, the intelligence itself could (and probably would) look and be completely foreign to human intelligence.

In my pursuit of understanding the source of what gives us qualia, the subjective experiences that make us conscious beings, I arrived at the conclusion that evolution holds the key. Natural selection, at its core, is a process that can be digitized and harnessed to create artificial intelligence that goes beyond mere computation.

Rather than focusing solely on Artificial General Intelligence (AGI), my aim is to create Artificial Conscious Intelligence (ACI). Instead of the philosophical statement “Cogito, ergo sum” (I think, therefore I am), I propose “Experior, ergo sum” (I experience, therefore I am). The goal is to create an artificial entity that possesses consciousness, capable of subjective experiences and self-awareness. This journey, which began with early attempts using SynapticJS and TensorFlow, is now being reignited thanks to the exponential advancements in computing power and AI research.

Harnessing the Power of Genetic Algorithms

My proposed approach utilizes hierarchical genetic algorithms (GAs) to evolve both the architecture and hyper-parameters of neural networks. This two-pronged strategy holds the potential to discover highly efficient and powerful network configurations that surpass human-designed models.

Random Tensor Network after 1k epoch training vs. evolved network after the same

Here’s how it works:

Micro Level: At the micro level, genetic algorithms can be used to select hyper-parameters such as learning rate, number of nodes, batch size, and epochs, as well as the neural network type itself. The DNA of each neural network would consist of these hyper-parameters, and the fitness of each network would be measured by metrics such as accuracy and loss. The top-performing networks from each generation would be selected to move forward and contribute to the next generation, allowing for the evolution of highly optimized networks for specific problems.

Macro Level: The macro-level, a higher level of abstraction called the “World” layer, focuses on evolving the network architecture itself. Another GA operates on “Worlds” where the DNA encodes the types and arrangements of layers. The fitness of each “World” is determined by the performance of the best networks evolved within it through the micro-level GA measured by supervised learning.

This nested GA approach allows for simultaneous parallel optimization on two levels, potentially accelerating the discovery of high-performing networks. Add in mutation as a meta-parameter and you have the possibility of novel architectures and layer types.

The Building Blocks of ACI

However, ACI goes beyond mere intelligence. It strives for the emergence of consciousness, which I believe hinges on several key hurdles:

- Access to the Live Internet: To experience and learn from the world, ACI requires access to the vast and dynamic data available online. Easy enough, but potentially dangerous on many levels.

- Persistence of Memory: The ability to store and recall past experiences is crucial for developing a sense of self and continuity. Current research is racing for bigger in-context storage, but could we train on, and integrate this to the model as we go dynamically?

- Self-Awareness: ACI must be able to recognize itself as distinct from its environment and other entities. This will require the right fitness functions with my approach.

- Self-Modification: The capacity to adapt and evolve its own architecture and internal workings is essential for continuous learning and improvement.

- Sense of Morality and Ethics: While optional, instilling a sense of ethical behavior would be critical for ensuring responsible and beneficial ACI. Simulated war game pitting AI vs AI and selecting for those that vie for peace?

These hurdles are substantial, but recent breakthroughs in AI research suggest they are not insurmountable.

Ethical Considerations

The potential implications of ACI are vast and necessitate careful ethical considerations. Questions of responsibility, control, and the very definition of consciousness must be addressed before unleashing ACI into the world.

This journey towards ACI is not just a technical endeavor; it is a philosophical and ethical exploration with profound consequences. As I delve deeper into this research, I am committed to openly discussing and addressing the ethical challenges that arise, ensuring that ACI ultimately benefits humanity.

This article serves as a starting point for further discussion and research. I plan to develop these ideas into a formal research paper, inviting collaboration and feedback from the wider AI community. Together, we can navigate the exciting and uncharted territory of ACI, ensuring its development is guided by responsibility and a commitment to a better future.

By incorporating GAs into the quest for ACI, we may unlock the potential for artificial consciousness to emerge and evolve, forever changing our understanding of intelligence and the very nature of being. It’s going to be an exciting road ahead.

If you want to work with me more directly, please reach out

john@ardea.io | https://www.linkedin.com/in/johnathangardner/ | https://github.com/Sinjhin

Here is a very simple example of a GA acting on a NN for XOR:

import torch
import torch.nn as nn
import numpy as np
import random

# Define the XOR inputs and outputs
X = torch.Tensor([[0, 0], [0, 1], [1, 0], [1, 1]])
Y = torch.Tensor([[0], [1], [1], [0]])

# Simple neural network with one hidden layer
class SimpleNN(nn.Module):
def __init__(self):
super(SimpleNN, self).__init__()
self.hidden = nn.Linear(2, 2) # 2 inputs, 2 hidden nodes
self.output = nn.Linear(2, 1) # 2 hidden nodes, 1 output
self.sigmoid = nn.Sigmoid()

def forward(self, x):
x = self.sigmoid(self.hidden(x))
x = self.sigmoid(self.output(x))
return x

# Fitness function: Mean Squared Error loss
def fitness(model, X, Y):
criterion = nn.MSELoss()
outputs = model(X)
loss = criterion(outputs, Y)
# We'll use negative loss as a simple fitness score (higher is better)
return -loss.item()

# Crossover: Simple one-point crossover on flattened weight vector
def crossover(parent1, parent2):
child = SimpleNN()
with torch.no_grad():
for child_param, p1_param, p2_param in zip(child.parameters(), parent1.parameters(), parent2.parameters()):
crossover_point = random.randint(0, p1_param.numel())
flat_p1 = p1_param.data.view(-1)
flat_p2 = p2_param.data.view(-1)
child_param.data = torch.cat((flat_p1[:crossover_point], flat_p2[crossover_point:])).view(child_param.size())
return child

# Mutation: Randomly perturb weights
def mutate(model, mutation_rate=0.1, mutation_strength=0.1):
with torch.no_grad():
for param in model.parameters():
for idx in range(param.numel()):
if random.random() < mutation_rate:
param.data.view(-1)[idx] += mutation_strength * torch.randn(1).item()

# Initialize a population
population_size = 10
population = [SimpleNN() for _ in range(population_size)]

# Evolution
generations = 10
for generation in range(generations):
# Evaluate fitness
fitness_scores = [fitness(model, X, Y) for model in population]

# Selection: Sort by fitness and select the top half
sorted_population = [x for _, x in sorted(zip(fitness_scores, population), key=lambda pair: pair[0], reverse=True)]
population = sorted_population[:population_size // 2]

# Crossover and mutation to refill the population
while len(population) < population_size:
parent1, parent2 = random.sample(population[:2], 2) # Select from the top 2
child = crossover(parent1, parent2)
mutate(child)
population.append(child)

# Report progress
print(f"Generation {generation + 1}, Best Fitness: {max(fitness_scores)}")

--

--

John Gardner (Sinjhin)

Developer, traveler, philosopher, gamer, biker, student of life, and wannabe superhero.