SelfTrainingClassifier on a Pipeline

### Describe the bug

SelfTrainingClassifier cannot be fit on text data even if the base_estimator parameter is an estimator that can accept text data (e.g. a pipeline with text preprocessing). In particular, it seems that SelfTrainingClassifier validates the data on the classifier within the pipeline (hence being unable to accept text) instead of validating on the entire pipeline (which can accept text).

### Steps/Code to Reproduce

```python
import pandas as pd
from sklearn.datasets import fetch_20newsgroups
from sklearn.compose import ColumnTransformer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.pipeline import Pipeline
from sklearn.naive_bayes import MultinomialNB
from sklearn.semi_supervised import SelfTrainingClassifier

#pulling text data
categories = ['alt.atheism', 'soc.religion.christian']
twenty_train = fetch_20newsgroups(subset='train', categories=categories, shuffle=True, random_state=42)

X = pd.DataFrame({'text': twenty_train.data})
y = twenty_train.target
#changing some labels to -1 for semi supervised
y[len(y) - 100:len(y)] = -1

#building pipeline
preprocessor = ColumnTransformer(
    transformers =  [
        ('vectorizer', CountVectorizer(), 'text')
    ])

classifier = MultinomialNB()

pipe = Pipeline(steps = [('preprocessor', preprocessor), ('classifier', classifier)])

#fitting SelfTrainingClassifier with the pipeline as the base estimator
stc = SelfTrainingClassifier(pipe)
stc.fit(X,y)

#note that fitting pipe does work
#pipe.fit(X,y)
```

### Expected Results

No error is thrown.

### Actual Results

```python-traceback
ValueError                                Traceback (most recent call last)
Input In [27], in <cell line: 30>()
     28 #fitting SelfTrainingClassifier with the pipeline as the base estimator
     29 stc = SelfTrainingClassifier(pipe)
---> 30 stc.fit(X,y)

File ~/code/scikit-learn/sklearn/semi_supervised/_self_training.py:181, in SelfTrainingClassifier.fit(self, X, y)
    162 """
    163 Fit self-training classifier using `X`, `y` as training data.
    164 
   (...)
    177     Fitted estimator.
    178 """
    179 # we need row slicing support for sparce matrices, but costly finiteness check
    180 # can be delegated to the base estimator.
--> 181 X, y = self._validate_data(
    182     X, y, accept_sparse=["csr", "csc", "lil", "dok"], force_all_finite=False
    183 )
    185 if self.base_estimator is None:
    186     raise ValueError("base_estimator cannot be None!")

File ~/code/scikit-learn/sklearn/base.py:596, in BaseEstimator._validate_data(self, X, y, reset, validate_separately, **check_params)
    594         y = check_array(y, input_name="y", **check_y_params)
    595     else:
--> 596         X, y = check_X_y(X, y, **check_params)
    597     out = X, y
    599 if not no_val_X and check_params.get("ensure_2d", True):

File ~/code/scikit-learn/sklearn/utils/validation.py:1074, in check_X_y(X, y, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, multi_output, ensure_min_samples, ensure_min_features, y_numeric, estimator)
   1069         estimator_name = _check_estimator_name(estimator)
   1070     raise ValueError(
   1071         f"{estimator_name} requires y to be passed, but the target y is None"
   1072     )
-> 1074 X = check_array(
   1075     X,
   1076     accept_sparse=accept_sparse,
   1077     accept_large_sparse=accept_large_sparse,
   1078     dtype=dtype,
   1079     order=order,
   1080     copy=copy,
   1081     force_all_finite=force_all_finite,
   1082     ensure_2d=ensure_2d,
   1083     allow_nd=allow_nd,
   1084     ensure_min_samples=ensure_min_samples,
   1085     ensure_min_features=ensure_min_features,
   1086     estimator=estimator,
   1087     input_name="X",
   1088 )
   1090 y = _check_y(y, multi_output=multi_output, y_numeric=y_numeric, estimator=estimator)
   1092 check_consistent_length(X, y)

File ~/code/scikit-learn/sklearn/utils/validation.py:856, in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator, input_name)
    854         array = array.astype(dtype, casting="unsafe", copy=False)
    855     else:
--> 856         array = np.asarray(array, order=order, dtype=dtype)
    857 except ComplexWarning as complex_warning:
    858     raise ValueError(
    859         "Complex data not supported\n{}\n".format(array)
    860     ) from complex_warning

File ~/mambaforge/envs/dev/lib/python3.10/site-packages/pandas/core/generic.py:2064, in NDFrame.__array__(self, dtype)
   2063 def __array__(self, dtype: npt.DTypeLike | None = None) -> np.ndarray:
-> 2064     return np.asarray(self._values, dtype=dtype)

ValueError: could not convert string to float: 'From: [...]'
```

### Versions

```shell
System:
    python: 3.10.4 

Python dependencies:
          pip: 22.0.4
   setuptools: 62.1.0
      sklearn: 1.0.2
        numpy: 1.21.5
        scipy: 1.7.3
       Cython: None
       pandas: 1.4.2
   matplotlib: None
       joblib: 1.1.0
threadpoolctl: 3.1.0

Built with OpenMP: True
```

COMMENT:
When initially fit, the base model is fit on the vectorized matrix of the the labeled set only. But at each iteration previously unlabeled data is added as  labeled data to the training set. In this step new words could potentially be introduced, and we'd want our input matrix to have these new words included. Whereas if we vectorize on the entire set of labeled and unlabeled data, the first several iterations could be fit using a matrix with 0 columns as some words may not have appeared in the training set yet. At least this is how I'm seeing it.

Labels: Bug, module:semi_supervised