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:
I am not following.

Given some text, `CountVectorizer` will provide a matrix of token counts. Therefore, it is independent of having an associated label or not, for the documents.

`SelfTrainingClassifier` will label at each iteration, therefore modifying the `y` variable (indeed changing the label tagged as `-1`) to learn an association between the matrix of token counts and `y`. The matrix of token counts will never change at this stage and vectorizing within the `SelfTrainingClassifier` will provide the exact same `X` matrix.

So I don't see what you mean by vectorizing at each iteration of the `SelfTrainingClassifier`?

Labels: Bug, module:semi_supervised